Skip to content

Commit e32105e

Browse files
authored
Merge pull request swiftlang#40392 from atrick/destroy-hoist
2 parents 6645f0d + c8a2130 commit e32105e

File tree

14 files changed

+1123
-30
lines changed

14 files changed

+1123
-30
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
232232
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
233233
}
234234

235+
/// Return true if \p instruction is a deinitialization barrier.
236+
///
237+
/// Deinitialization barriers constrain variable lifetimes. Lexical end_borrow
238+
/// and destroy_addr cannot be hoisted above them.
239+
bool isDeinitBarrier(SILInstruction *instruction);
240+
235241
} // end namespace swift
236242

237243
//===----------------------------------------------------------------------===//
@@ -1303,9 +1309,8 @@ struct AccessUseVisitor {
13031309
///
13041310
/// Return true if all uses were collected. This is always true as long the \p
13051311
/// visitor's visitUse method returns true.
1306-
bool visitAccessStorageUses(AccessUseVisitor &visitor,
1307-
AccessStorage storage,
1308-
SILFunction *function);
1312+
bool visitAccessStorageUses(AccessUseVisitor &visitor, AccessStorage storage,
1313+
SILFunction *function);
13091314

13101315
/// Visit the uses of \p accessPath.
13111316
///
@@ -1319,6 +1324,45 @@ bool visitAccessPathUses(AccessUseVisitor &visitor, AccessPath accessPath,
13191324

13201325
} // end namespace swift
13211326

1327+
//===----------------------------------------------------------------------===//
1328+
// MARK: UniqueAddressUses
1329+
//===----------------------------------------------------------------------===//
1330+
1331+
namespace swift {
1332+
1333+
/// Analyze and classify the leaf uses of unique storage.
1334+
///
1335+
/// Storage that has a unique set of roots within this function includes
1336+
/// alloc_stack, alloc_box, exclusive argument, and global variables. All access
1337+
/// to the storage within this function is derived from these roots.
1338+
///
1339+
/// Gather the kinds of uses that are typically relevant to algorithms:
1340+
/// - loads (including copies out of, not including inout args)
1341+
/// - stores (including copies into and inout args)
1342+
/// - destroys (of the entire aggregate)
1343+
/// - debugUses (only populated when preserveDebugInfo == false)
1344+
/// - unknownUses (e.g. address_to_pointer, box escape)
1345+
struct UniqueStorageUseVisitor {
1346+
static bool findUses(UniqueStorageUseVisitor &visitor);
1347+
1348+
SILFunction *function;
1349+
AccessStorage storage;
1350+
1351+
UniqueStorageUseVisitor(AccessStorage storage, SILFunction *function)
1352+
: function(function), storage(storage) {}
1353+
1354+
virtual ~UniqueStorageUseVisitor() = default;
1355+
1356+
virtual bool visitLoad(Operand *use) = 0;
1357+
virtual bool visitStore(Operand *use) = 0;
1358+
virtual bool visitDestroy(Operand *use) = 0;
1359+
virtual bool visitDealloc(Operand *use) = 0;
1360+
virtual bool visitDebugUse(Operand *use) = 0;
1361+
virtual bool visitUnknownUse(Operand *use) = 0;
1362+
};
1363+
1364+
} // namespace swift
1365+
13221366
//===----------------------------------------------------------------------===//
13231367
// MARK: Helper API for specific formal access patterns
13241368
//===----------------------------------------------------------------------===//

include/swift/SIL/SILFunction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,15 @@ class SILFunction
839839
OptMode = unsigned(mode);
840840
}
841841

842+
/// True if debug information must be preserved (-Onone).
843+
///
844+
/// If this is false (-O), then the presence of debug info must not affect the
845+
/// outcome of any transformations.
846+
///
847+
/// Typically used to determine whether a debug_value is a normal SSA use or
848+
/// incidental use.
849+
bool preserveDebugInfo() const;
850+
842851
PerformanceConstraints getPerfConstraints() const { return perfConstraints; }
843852

844853
void setPerfConstraints(PerformanceConstraints perfConstr) {

include/swift/SIL/SILInstruction.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,14 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
459459
locationStorage = loc.storage;
460460
}
461461

462+
/// Return the next instruction or nullptr if this is the last instruction in
463+
/// its block.
464+
SILInstruction *getPreviousInstruction();
465+
466+
/// Return the previous instruction or nullptr if this is the first
467+
/// instruction in its block.
468+
SILInstruction *getNextInstruction();
469+
462470
/// This method unlinks 'self' from the containing basic block and deletes it.
463471
void eraseFromParent();
464472

@@ -1013,6 +1021,14 @@ class SingleValueInstruction : public SILInstruction, public ValueBase {
10131021
/// If this is an instruction which "defines" an opened archetype, it is
10141022
/// returned.
10151023
CanArchetypeType getOpenedArchetype() const;
1024+
1025+
SILInstruction *getPreviousInstruction() {
1026+
return SILInstruction::getPreviousInstruction();
1027+
}
1028+
1029+
SILInstruction *getNextInstruction() {
1030+
return SILInstruction::getNextInstruction();
1031+
}
10161032
};
10171033

10181034
struct SILNodeOffsetChecker {
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===--- Reachability.h ---------------------------------------------------===//
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+
/// Reachability data flow analysis using a path-discovery worklist. For
14+
/// efficient data flow propagation based on a single SSA value and its uses.
15+
///
16+
/// TODO: Add an optimistic data flow for more aggresive optimization:
17+
/// - Add another set for blocks reachable by barriers
18+
/// - Change the meet operation to a union
19+
/// - Propagate past barriers up to the SSA def
20+
/// - Iterate to a fix-point.
21+
///
22+
//===----------------------------------------------------------------------===//
23+
24+
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H
25+
#define SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H
26+
27+
#include "swift/SIL/BasicBlockDatastructures.h"
28+
#include "swift/SIL/SILBasicBlock.h"
29+
30+
namespace swift {
31+
32+
/// Pessimistic, non-iterative data flow for analyzing backward reachability
33+
/// from a set of last uses to their dominating def or nearest barrier.
34+
///
35+
/// Intended for frequently called utilities where minimizing the cost of data
36+
/// flow is more important than analyzing reachability across loops. Expected to
37+
/// visit very few blocks because barriers often occur close to a last use.
38+
///
39+
/// BlockReachability {
40+
/// // True if the beginning of \p block is reachable.
41+
/// // Typically a BasicBlockSet wrapper.
42+
/// bool hasReachableBegin(SILBasicBlock *block);
43+
///
44+
/// // Mark the beginning of a block reachable. Only called once per block.
45+
/// // Typically a BasicBlockSet wrapper.
46+
/// boid markReachableBegin(SILBasicBlock *block);
47+
///
48+
/// // Mark the end of a block reachable. Only called once per block.
49+
/// // Typically a BasicBlockSet wrapper.
50+
/// void markReachableEnd(SILBasicBlock *block);
51+
///
52+
/// // Return true if \p inst is a barrier. Called once for each reachable
53+
/// // instruction, assuming that each lastUse is itself a barrier.
54+
/// // Used by the data flow client to perform additional book-keeping,
55+
/// // such as recording debug_value instructions.
56+
/// bool checkReachableBarrier(SILInstruction *inst);
57+
/// };
58+
template <typename BlockReachability>
59+
class BackwardReachability {
60+
SILFunction *function;
61+
BlockReachability &reachableBlocks;
62+
BasicBlockWorklist cfgWorklist;
63+
64+
public:
65+
BackwardReachability(SILFunction *function,
66+
BlockReachability &reachableBlocks)
67+
: function(function), reachableBlocks(reachableBlocks),
68+
cfgWorklist(function) {}
69+
70+
// Initialize data flow starting points before running solveBackward.
71+
void initLastUse(SILInstruction *lastUsePoint) {
72+
auto *lastUseBlock = lastUsePoint->getParent();
73+
if (canReachBlockBegin(lastUsePoint)) {
74+
pushPreds(lastUseBlock);
75+
}
76+
}
77+
78+
// Data flow "meet": interesection of successor reachability.
79+
void solveBackward() {
80+
while (SILBasicBlock *block = cfgWorklist.popAndForget()) {
81+
if (!meetOverSuccessors(block))
82+
continue;
83+
84+
reachableBlocks.markReachableEnd(block);
85+
86+
if (canReachBlockBegin(block->getTerminator())) {
87+
pushPreds(block);
88+
}
89+
}
90+
}
91+
92+
protected:
93+
BackwardReachability(BackwardReachability const &) = delete;
94+
BackwardReachability &operator=(BackwardReachability const &) = delete;
95+
96+
// Perform a "meet" over successor begin reachability.
97+
// Return true if \p predecessor's end is pessimistically reachable.
98+
//
99+
// Meet:
100+
// ReachableEnd(predecessor) := intersection(ReachableBegin, successors)
101+
bool meetOverSuccessors(SILBasicBlock *block) {
102+
return llvm::all_of(block->getSuccessorBlocks(), [this](auto *successor) {
103+
return reachableBlocks.hasReachableBegin(successor);
104+
});
105+
}
106+
107+
// Local data flow. Computes the block's flow function.
108+
bool canReachBlockBegin(SILInstruction *lastReachablePoint) {
109+
do {
110+
if (reachableBlocks.checkReachableBarrier(lastReachablePoint))
111+
return false;
112+
lastReachablePoint = lastReachablePoint->getPreviousInstruction();
113+
} while (lastReachablePoint);
114+
return true;
115+
}
116+
117+
// Propagate global data flow from \p succBB to its predecessors.
118+
void pushPreds(SILBasicBlock *succBB) {
119+
reachableBlocks.markReachableBegin(succBB);
120+
121+
for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) {
122+
cfgWorklist.pushIfNotVisited(predBB);
123+
}
124+
}
125+
};
126+
127+
} // end namespace swift
128+
129+
#endif

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ PASS(DefiniteInitialization, "definite-init",
193193
"Definite Initialization for Diagnostics")
194194
PASS(DestroyHoisting, "destroy-hoisting",
195195
"Hoisting of value destroys")
196+
PASS(SSADestroyHoisting, "ssa-destroy-hoisting",
197+
"Hoist destroy_addr for uniquely identified values")
196198
PASS(Devirtualizer, "devirtualizer",
197199
"Indirect Call Devirtualization")
198200
PASS(DiagnoseInfiniteRecursion, "diagnose-infinite-recursion",

lib/SIL/IR/SILFunction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ OptimizationMode SILFunction::getEffectiveOptimizationMode() const {
281281
return getModule().getOptions().OptMode;
282282
}
283283

284+
bool SILFunction::preserveDebugInfo() const {
285+
return getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization;
286+
}
287+
284288
bool SILFunction::shouldOptimize() const {
285289
return getEffectiveOptimizationMode() != OptimizationMode::NoOptimization;
286290
}

lib/SIL/IR/SILInstruction.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ SILModule &SILInstruction::getModule() const {
103103
return getFunction()->getModule();
104104
}
105105

106+
SILInstruction *SILInstruction::getPreviousInstruction() {
107+
auto pos = getIterator();
108+
return pos == getParent()->begin() ? nullptr : &*std::prev(pos);
109+
}
110+
111+
SILInstruction *SILInstruction::getNextInstruction() {
112+
auto nextPos = std::next(getIterator());
113+
return nextPos == getParent()->end() ? nullptr : &*nextPos;
114+
}
115+
106116
void SILInstruction::removeFromParent() {
107117
#ifndef NDEBUG
108118
for (auto result : getResults()) {

0 commit comments

Comments
 (0)