Skip to content

Commit 0b3027a

Browse files
committed
Add destroy_value [poison] support to mandatory copy propagation.
Ensure that any object lifetime that will be shortened ends in destroy_value [poison], indicating that the reference should not be dereferenced (e.g. by the debugger) beyond this point. This has no way of knowing whether the object will actually be deallocated at this point. It conservatively avoids showing garbage to debuggers. Part 1/2: rdar://75012368 (-Onone compiler support for early object deinitialization with sentinel dead references)
1 parent e7cbf1d commit 0b3027a

File tree

3 files changed

+236
-37
lines changed

3 files changed

+236
-37
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#ifndef SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H
9494
#define SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H
9595

96+
#include "swift/Basic/SmallPtrSetVector.h"
9697
#include "swift/SIL/SILInstruction.h"
9798
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
9899
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
@@ -123,13 +124,33 @@ class CanonicalOSSAConsumeInfo {
123124
/// that should be canonicalized separately.
124125
llvm::SmallDenseMap<SILBasicBlock *, CopyValueInst *, 4> persistentCopies;
125126

127+
/// The set of non-destroy consumes that need to be poisoned. This is
128+
/// determined in two steps. First findOrInsertDestroyInBlock() checks if the
129+
/// lifetime shrank within the block. Second rewriteCopies() checks if the
130+
/// consume is in remnantLiveOutBlock(). Finally injectPoison() inserts new
131+
/// copies and poison destroys for everything in this set.
132+
SmallPtrSetVector<SILInstruction *, 4> needsPoisonConsumes;
133+
126134
public:
127135
bool hasUnclaimedConsumes() const { return !finalBlockConsumes.empty(); }
128136

129137
void clear() {
130138
finalBlockConsumes.clear();
131139
debugAfterConsume.clear();
132140
persistentCopies.clear();
141+
needsPoisonConsumes.clear();
142+
}
143+
144+
void recordNeedsPoison(SILInstruction *consume) {
145+
needsPoisonConsumes.insert(consume);
146+
}
147+
148+
bool needsPoison(SILInstruction *consume) const {
149+
return needsPoisonConsumes.count(consume);
150+
}
151+
152+
ArrayRef<SILInstruction *> getNeedsPoisonConsumes() const {
153+
return needsPoisonConsumes.getArrayRef();
133154
}
134155

135156
bool hasFinalConsumes() const { return !finalBlockConsumes.empty(); }
@@ -158,6 +179,11 @@ class CanonicalOSSAConsumeInfo {
158179
debugAfterConsume.push_back(dvi);
159180
}
160181

182+
void popDebugAfterConsume(DebugValueInst *dvi) {
183+
if (!debugAfterConsume.empty() && debugAfterConsume.back() == dvi)
184+
debugAfterConsume.pop_back();
185+
}
186+
161187
ArrayRef<DebugValueInst *> getDebugInstsAfterConsume() const {
162188
return debugAfterConsume;
163189
}
@@ -189,6 +215,9 @@ class CanonicalizeOSSALifetime {
189215
/// liveness may be pruned during canonicalization.
190216
bool pruneDebugMode;
191217

218+
/// If true, then new destroy_value instructions will be poison.
219+
bool poisonRefsMode;
220+
192221
NonLocalAccessBlockAnalysis *accessBlockAnalysis;
193222
// Lazily initialize accessBlocks only when
194223
// extendLivenessThroughOverlappingAccess is invoked.
@@ -232,16 +261,26 @@ class CanonicalizeOSSALifetime {
232261
/// ending". end_borrows do not end a liverange that may include owned copies.
233262
PrunedLiveness liveness;
234263

264+
/// remnantLiveOutBlocks are part of the original extended lifetime that are
265+
/// not in canonical pruned liveness. There is a path from a PrunedLiveness
266+
/// boundary to an original destroy that passes through this block.
267+
///
268+
/// These blocks would be equivalent to PrunedLiveness::LiveOut if
269+
/// PrunedLiveness were recomputed using all original destroys as interesting
270+
/// uses, minus blocks already marked PrunedLiveness::LiveOut. (These blocks
271+
/// may be in PrunedLiveness::LiveWithin).
272+
SmallSetVector<SILBasicBlock *, 8> remnantLiveOutBlocks;
273+
235274
/// Information about consuming instructions discovered in this caonical OSSA
236275
/// lifetime.
237276
CanonicalOSSAConsumeInfo consumes;
238277

239278
public:
240-
CanonicalizeOSSALifetime(bool pruneDebugMode,
279+
CanonicalizeOSSALifetime(bool pruneDebugMode, bool poisonRefsMode,
241280
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
242281
DominanceAnalysis *dominanceAnalysis,
243282
DeadEndBlocks *deBlocks)
244-
: pruneDebugMode(pruneDebugMode),
283+
: pruneDebugMode(pruneDebugMode), poisonRefsMode(poisonRefsMode),
245284
accessBlockAnalysis(accessBlockAnalysis),
246285
dominanceAnalysis(dominanceAnalysis), deBlocks(deBlocks) {}
247286

@@ -263,6 +302,7 @@ class CanonicalizeOSSALifetime {
263302
consumingBlocks.clear();
264303
debugValues.clear();
265304
liveness.clear();
305+
remnantLiveOutBlocks.clear();
266306
}
267307

268308
bool hasChanged() const { return changed; }
@@ -309,7 +349,12 @@ class CanonicalizeOSSALifetime {
309349

310350
void findOrInsertDestroys();
311351

352+
void insertDestroyOnCFGEdge(SILBasicBlock *predBB, SILBasicBlock *succBB,
353+
bool needsPoison);
354+
312355
void rewriteCopies();
356+
357+
void injectPoison();
313358
};
314359

315360
} // end namespace swift

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,13 @@ class CopyPropagation : public SILFunctionTransform {
4444
bool pruneDebug;
4545
/// True of all values should be canonicalized.
4646
bool canonicalizeAll;
47+
/// If true, then new destroy_value instructions will be poison.
48+
bool poisonRefs;
4749

4850
public:
49-
CopyPropagation(bool pruneDebug, bool canonicalizeAll)
50-
: pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll) {}
51+
CopyPropagation(bool pruneDebug, bool canonicalizeAll, bool poisonRefs)
52+
: pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
53+
poisonRefs(poisonRefs) {}
5154

5255
/// The entry point to this function transformation.
5356
void run() override;
@@ -98,7 +101,8 @@ void CopyPropagation::run() {
98101
}
99102
}
100103
// Perform copy propgation for each copied value.
101-
CanonicalizeOSSALifetime canonicalizer(pruneDebug, accessBlockAnalysis,
104+
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
105+
accessBlockAnalysis,
102106
dominanceAnalysis,
103107
deBlocksAnalysis->get(f));
104108
// Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the
@@ -138,9 +142,12 @@ void CopyPropagation::run() {
138142
}
139143

140144
SILTransform *swift::createMandatoryCopyPropagation() {
141-
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true);
145+
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true,
146+
/*poisonRefs*/ true);
142147
}
143148

144149
SILTransform *swift::createCopyPropagation() {
145-
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ false);
150+
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ false,
151+
/*poisonRefs*/ false);
146152
}
153+

0 commit comments

Comments
 (0)