Skip to content

Commit 2a6d271

Browse files
Merge pull request swiftlang#74134 from nate-chandler/cherrypick/release/6.0/rdar113142446
6.0: [ConsumeObjectChecker] End lifetimes at consumes.
2 parents ae12ced + 334b988 commit 2a6d271

File tree

12 files changed

+647
-57
lines changed

12 files changed

+647
-57
lines changed

include/swift/SIL/ParseTestSpecification.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,15 @@ struct Arguments {
186186
}
187187
Operand *takeOperand() { return takeInstance<OperandArgument>("operand"); }
188188
SILInstruction *takeInstruction() {
189-
return takeInstance<InstructionArgument>("instruction");
189+
auto argument = takeArgument();
190+
if (isa<ValueArgument>(argument)) {
191+
auto value = cast<ValueArgument>(argument).getValue();
192+
auto *definingInstruction = value.getDefiningInstruction();
193+
assert(definingInstruction &&
194+
"selected instruction via argument value!?");
195+
return definingInstruction;
196+
}
197+
return getInstance<InstructionArgument>("instruction", argument);
190198
}
191199
SILArgument *takeBlockArgument() {
192200
return takeInstance<BlockArgumentArgument>("block argument");

include/swift/SIL/PrunedLiveness.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,9 +613,10 @@ class PrunedLiveRange : public PrunedLiveness {
613613
ValueSet &visited,
614614
SILValue value);
615615

616-
void updateForUse(SILInstruction *user, LifetimeEnding lifetimeEnding);
617-
618616
public:
617+
/// Add \p inst to liveness which uses the def as indicated by \p usage.
618+
void updateForUse(SILInstruction *inst, LifetimeEnding usage);
619+
619620
/// For flexibility, \p lifetimeEnding is provided by the
620621
/// caller. PrunedLiveness makes no assumptions about the def-use
621622
/// relationships that generate liveness. For example, use->isLifetimeEnding()

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,16 @@ class CanonicalOSSAConsumeInfo final {
180180
SWIFT_ASSERT_ONLY_DECL(void dump() const LLVM_ATTRIBUTE_USED);
181181
};
182182

183+
enum PruneDebugInsts_t : bool {
184+
DontPruneDebugInsts = false,
185+
PruneDebugInsts = true,
186+
};
187+
188+
enum MaximizeLifetime_t : bool {
189+
DontMaximizeLifetime = false,
190+
MaximizeLifetime = true,
191+
};
192+
183193
/// Canonicalize OSSA lifetimes.
184194
///
185195
/// Allows the allocation of analysis state to be reused across calls to
@@ -221,11 +231,11 @@ class CanonicalizeOSSALifetime final {
221231
private:
222232
/// If true, then debug_value instructions outside of non-debug
223233
/// liveness may be pruned during canonicalization.
224-
bool pruneDebugMode;
234+
const PruneDebugInsts_t pruneDebugMode;
225235

226236
/// If true, lifetimes will not be shortened except when necessary to avoid
227237
/// copies.
228-
bool maximizeLifetime;
238+
const MaximizeLifetime_t maximizeLifetime;
229239

230240
// If present, will be used to ensure that the lifetime is not shortened to
231241
// end inside an access scope which it previously enclosed. (Note that ending
@@ -246,6 +256,9 @@ class CanonicalizeOSSALifetime final {
246256
/// The SILValue to canonicalize.
247257
SILValue currentDef;
248258

259+
/// Instructions beyond which liveness is not extended by destroy uses.
260+
ArrayRef<SILInstruction *> currentLexicalLifetimeEnds;
261+
249262
/// Original points in the CFG where the current value's lifetime is consumed
250263
/// or destroyed. Each block either contains a consuming instruction (e.g.
251264
/// `destroy_value`) or is on the availability boundary of the value in a
@@ -306,13 +319,15 @@ class CanonicalizeOSSALifetime final {
306319
struct LivenessState {
307320
BitfieldRef<SSAPrunedLiveness>::StackState state;
308321

309-
LivenessState(CanonicalizeOSSALifetime &parent, SILValue def)
322+
LivenessState(CanonicalizeOSSALifetime &parent, SILValue def,
323+
ArrayRef<SILInstruction *> lexicalLifetimeEnds)
310324
: state(parent.liveness, def->getFunction()) {
311-
parent.initializeLiveness(def);
325+
parent.initializeLiveness(def, lexicalLifetimeEnds);
312326
}
313327
};
314328

315-
CanonicalizeOSSALifetime(bool pruneDebugMode, bool maximizeLifetime,
329+
CanonicalizeOSSALifetime(PruneDebugInsts_t pruneDebugMode,
330+
MaximizeLifetime_t maximizeLifetime,
316331
SILFunction *function,
317332
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
318333
DominanceInfo *domTree,
@@ -324,7 +339,8 @@ class CanonicalizeOSSALifetime final {
324339

325340
SILValue getCurrentDef() const { return currentDef; }
326341

327-
void initializeLiveness(SILValue def) {
342+
void initializeLiveness(SILValue def,
343+
ArrayRef<SILInstruction *> lexicalLifetimeEnds) {
328344
assert(consumingBlocks.empty() && debugValues.empty());
329345
// Clear the cached analysis pointer just in case the client invalidates the
330346
// analysis, freeing its memory.
@@ -333,6 +349,7 @@ class CanonicalizeOSSALifetime final {
333349
destroys.clear();
334350

335351
currentDef = def;
352+
currentLexicalLifetimeEnds = lexicalLifetimeEnds;
336353

337354
if (maximizeLifetime || respectsDeinitBarriers()) {
338355
liveness->initializeDiscoveredBlocks(&discoveredBlocks);
@@ -347,8 +364,18 @@ class CanonicalizeOSSALifetime final {
347364
}
348365

349366
/// Top-Level API: rewrites copies and destroys within \p def's extended
350-
/// lifetime. \p lifetime caches transient analysis state across multiple
351-
/// calls.
367+
/// lifetime.
368+
///
369+
/// For lexical values, canonicalization respects deinit barriers, introducing
370+
/// copies as needed to maintain lifetimes beyond final consuming uses to the
371+
/// original lexical lifetime end. When a lexical value is explicitly
372+
/// consumed (via the `consume` keyword), however, the lifetime does not
373+
/// extend to original destroys beyond that consume--the value must be dead
374+
/// after the corresponding marker instructions (`move_value`); to support
375+
/// this shortening, the marker instructions must be provided as \p
376+
/// lexicalLifetimeEnds. When provided, deinit barriers will be respected
377+
/// except to the extent doing so would result in the value being live after
378+
/// the marker instructions.
352379
///
353380
/// Return true if any change was made to \p def's extended lifetime. \p def
354381
/// itself will not be deleted and no instructions outside of \p def's
@@ -358,7 +385,8 @@ class CanonicalizeOSSALifetime final {
358385
/// This only deletes instructions within \p def's extended lifetime. Use
359386
/// InstructionDeleter::cleanUpDeadInstructions() to recursively delete dead
360387
/// operands.
361-
bool canonicalizeValueLifetime(SILValue def);
388+
bool canonicalizeValueLifetime(
389+
SILValue def, ArrayRef<SILInstruction *> lexicalLifetimeEnds = {});
362390

363391
/// Compute the liveness information for \p def. But do not do any rewriting
364392
/// or computation of boundaries.
@@ -448,6 +476,10 @@ class CanonicalizeOSSALifetime final {
448476
void extendLivenessToDeinitBarriers();
449477

450478
void extendUnconsumedLiveness(PrunedLivenessBoundary const &boundary);
479+
void visitExtendedUnconsumedBoundary(
480+
ArrayRef<SILInstruction *> ends,
481+
llvm::function_ref<void(SILInstruction *, PrunedLiveness::LifetimeEnding)>
482+
visitor);
451483

452484
void insertDestroysOnBoundary(PrunedLivenessBoundary const &boundary);
453485

lib/SILOptimizer/Mandatory/ConsumeOperatorCopyableValuesChecker.cpp

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/SIL/SILInstruction.h"
2626
#include "swift/SIL/SILUndef.h"
2727
#include "swift/SILOptimizer/Analysis/Analysis.h"
28+
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
2829
#include "swift/SILOptimizer/Analysis/ClosureScope.h"
2930
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
3031
#include "swift/SILOptimizer/Analysis/LoopAnalysis.h"
@@ -216,17 +217,18 @@ namespace {
216217
struct ConsumeOperatorCopyableValuesChecker {
217218
SILFunction *fn;
218219
CheckerLivenessInfo livenessInfo;
219-
DominanceInfo *dominanceToUpdate;
220-
SILLoopInfo *loopInfoToUpdate;
221-
222-
ConsumeOperatorCopyableValuesChecker(SILFunction *fn)
223-
: fn(fn), dominanceToUpdate(nullptr), loopInfoToUpdate(nullptr) {}
224-
225-
void setDominanceToUpdate(DominanceInfo *newDFI) {
226-
dominanceToUpdate = newDFI;
227-
}
228-
229-
void setLoopInfoToUpdate(SILLoopInfo *newLFI) { loopInfoToUpdate = newLFI; }
220+
DominanceInfo *dominance;
221+
InstructionDeleter deleter;
222+
CanonicalizeOSSALifetime canonicalizer;
223+
224+
ConsumeOperatorCopyableValuesChecker(SILFunction *fn,
225+
DominanceInfo *dominance,
226+
BasicCalleeAnalysis *calleeAnalysis)
227+
: fn(fn), dominance(dominance),
228+
canonicalizer(DontPruneDebugInsts,
229+
MaximizeLifetime_t(!fn->shouldOptimize()), fn,
230+
/*accessBlockAnalysis=*/nullptr, dominance,
231+
calleeAnalysis, deleter) {}
230232

231233
bool check();
232234

@@ -480,6 +482,7 @@ bool ConsumeOperatorCopyableValuesChecker::check() {
480482
// Then look at all of our found consuming uses. See if any of these are
481483
// _move that are within the boundary.
482484
bool foundMove = false;
485+
SmallVector<SILInstruction *, 2> validMoves;
483486
auto dbgVarInst = DebugVarCarryingInst::getFromValue(lexicalValue);
484487
StringRef varName = DebugVarCarryingInst::getName(dbgVarInst);
485488
for (auto *use : livenessInfo.consumingUse) {
@@ -510,6 +513,7 @@ bool ConsumeOperatorCopyableValuesChecker::check() {
510513
dbgVarInst->getLoc(), SILUndef::get(mvi->getOperand()),
511514
*varInfo, false /*poison*/, UsesMoveableValueDebugInfo);
512515
}
516+
validMoves.push_back(mvi);
513517
}
514518
foundMove = true;
515519
}
@@ -520,6 +524,11 @@ bool ConsumeOperatorCopyableValuesChecker::check() {
520524
if (foundMove) {
521525
dbgVarInst.markAsMoved();
522526
}
527+
528+
if (validMoves.size() > 0) {
529+
canonicalizer.clear();
530+
canonicalizer.canonicalizeValueLifetime(lexicalValue, validMoves);
531+
}
523532
}
524533

525534
return false;
@@ -575,18 +584,16 @@ class ConsumeOperatorCopyableValuesCheckerPass : public SILFunctionTransform {
575584
LLVM_DEBUG(llvm::dbgs() << "*** Checking moved values in fn: "
576585
<< getFunction()->getName() << '\n');
577586

578-
ConsumeOperatorCopyableValuesChecker checker(getFunction());
579-
580-
// If we already had dominance or loop info generated, update them when
581-
// splitting blocks.
582587
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
583-
if (dominanceAnalysis->hasFunctionInfo(fn))
584-
checker.setDominanceToUpdate(dominanceAnalysis->get(fn));
588+
auto *dominance = dominanceAnalysis->get(fn);
589+
auto *calleeAnalysis = getAnalysis<BasicCalleeAnalysis>();
590+
ConsumeOperatorCopyableValuesChecker checker(getFunction(), dominance,
591+
calleeAnalysis);
585592
auto *loopAnalysis = getAnalysis<SILLoopAnalysis>();
586-
if (loopAnalysis->hasFunctionInfo(fn))
587-
checker.setLoopInfoToUpdate(loopAnalysis->get(fn));
588593

589594
if (checker.check()) {
595+
// If we already had dominance or loop info generated, update them when
596+
// splitting blocks.
590597
AnalysisPreserver preserveDominance(dominanceAnalysis);
591598
AnalysisPreserver preserveLoop(loopAnalysis);
592599
invalidateAnalysis(

lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ struct OSSACanonicalizer {
4848

4949
OSSACanonicalizer(SILFunction *fn, DominanceInfo *domTree,
5050
InstructionDeleter &deleter)
51-
: canonicalizer(false /*pruneDebugMode*/,
52-
!fn->shouldOptimize() /*maximizeLifetime*/, fn,
51+
: canonicalizer(DontPruneDebugInsts,
52+
MaximizeLifetime_t(!fn->shouldOptimize()), fn,
5353
nullptr /*accessBlockAnalysis*/, domTree,
5454
nullptr /*calleeAnalysis*/, deleter) {}
5555

@@ -64,7 +64,7 @@ struct OSSACanonicalizer {
6464
CanonicalizeOSSALifetime::LivenessState canonicalizerState;
6565

6666
LivenessState(OSSACanonicalizer &parent, SILValue def)
67-
: parent(parent), canonicalizerState(parent.canonicalizer, def) {}
67+
: parent(parent), canonicalizerState(parent.canonicalizer, def, {}) {}
6868

6969
~LivenessState() { parent.clear(); }
7070
};

lib/SILOptimizer/SILCombiner/SILCombine.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ void SILCombiner::canonicalizeOSSALifetimes(SILInstruction *currentInst) {
351351

352352
DominanceInfo *domTree = DA->get(&Builder.getFunction());
353353
CanonicalizeOSSALifetime canonicalizer(
354-
false /*prune debug*/,
355-
!parentTransform->getFunction()->shouldOptimize() /*maximize lifetime*/,
354+
DontPruneDebugInsts,
355+
MaximizeLifetime_t(!parentTransform->getFunction()->shouldOptimize()),
356356
parentTransform->getFunction(), NLABA, domTree, CA, deleter);
357357
CanonicalizeBorrowScope borrowCanonicalizer(parentTransform->getFunction(),
358358
deleter);

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,15 +425,15 @@ namespace {
425425

426426
class CopyPropagation : public SILFunctionTransform {
427427
/// If true, debug_value instructions should be pruned.
428-
bool pruneDebug;
428+
PruneDebugInsts_t pruneDebug;
429429
/// If true, all values will be canonicalized.
430430
bool canonicalizeAll;
431431
/// If true, then borrow scopes will be canonicalized, allowing copies of
432432
/// guaranteed values to be optimized. Does *not* shrink the borrow scope.
433433
bool canonicalizeBorrows;
434434

435435
public:
436-
CopyPropagation(bool pruneDebug, bool canonicalizeAll,
436+
CopyPropagation(PruneDebugInsts_t pruneDebug, bool canonicalizeAll,
437437
bool canonicalizeBorrows)
438438
: pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
439439
canonicalizeBorrows(canonicalizeBorrows) {}
@@ -493,7 +493,7 @@ void CopyPropagation::run() {
493493
// canonicalizer performs all modifications through deleter's callbacks, so we
494494
// don't need to explicitly check for changes.
495495
CanonicalizeOSSALifetime canonicalizer(
496-
pruneDebug, /*maximizeLifetime=*/!getFunction()->shouldOptimize(),
496+
pruneDebug, MaximizeLifetime_t(!getFunction()->shouldOptimize()),
497497
getFunction(), accessBlockAnalysis, domTree, calleeAnalysis, deleter);
498498
// NOTE: We assume that the function is in reverse post order so visiting the
499499
// blocks and pushing begin_borrows as we see them and then popping them
@@ -643,11 +643,11 @@ void CopyPropagation::run() {
643643
// MandatoryCopyPropagation is not currently enabled in the -Onone pipeline
644644
// because it may negatively affect the debugging experience.
645645
SILTransform *swift::createMandatoryCopyPropagation() {
646-
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true,
646+
return new CopyPropagation(PruneDebugInsts, /*canonicalizeAll*/ true,
647647
/*canonicalizeBorrows*/ false);
648648
}
649649

650650
SILTransform *swift::createCopyPropagation() {
651-
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true,
651+
return new CopyPropagation(PruneDebugInsts, /*canonicalizeAll*/ true,
652652
/*canonicalizeBorrows*/ EnableRewriteBorrows);
653653
}

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2070,7 +2070,7 @@ void MemoryToRegisters::canonicalizeValueLifetimes(
20702070
}
20712071
}
20722072
CanonicalizeOSSALifetime canonicalizer(
2073-
/*pruneDebug=*/true, /*maximizeLifetime=*/!f.shouldOptimize(), &f,
2073+
PruneDebugInsts, MaximizeLifetime_t(!f.shouldOptimize()), &f,
20742074
accessBlockAnalysis, domInfo, calleeAnalysis, deleter);
20752075
for (auto value : owned) {
20762076
if (isa<SILUndef>(value) || value->isMarkedAsDeleted())

0 commit comments

Comments
 (0)