Skip to content

Commit db5e0cf

Browse files
committed
[NFC] OwnedValueCan: Add support for end markers.
Enhance the utility with the ability to end lifetimes of lexical values at indicated instructions, overriding the usual behavior of maintaining such lifetimes' previous endpoints (modulo non-deinit-barrier instructions).
1 parent f83f437 commit db5e0cf

File tree

4 files changed

+337
-15
lines changed

4 files changed

+337
-15
lines changed

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ class CanonicalizeOSSALifetime final {
256256
/// The SILValue to canonicalize.
257257
SILValue currentDef;
258258

259+
/// Instructions beyond which liveness is not extended by destroy uses.
260+
ArrayRef<SILInstruction *> currentLexicalLifetimeEnds;
261+
259262
/// Original points in the CFG where the current value's lifetime is consumed
260263
/// or destroyed. Each block either contains a consuming instruction (e.g.
261264
/// `destroy_value`) or is on the availability boundary of the value in a
@@ -316,9 +319,10 @@ class CanonicalizeOSSALifetime final {
316319
struct LivenessState {
317320
BitfieldRef<SSAPrunedLiveness>::StackState state;
318321

319-
LivenessState(CanonicalizeOSSALifetime &parent, SILValue def)
322+
LivenessState(CanonicalizeOSSALifetime &parent, SILValue def,
323+
ArrayRef<SILInstruction *> lexicalLifetimeEnds)
320324
: state(parent.liveness, def->getFunction()) {
321-
parent.initializeLiveness(def);
325+
parent.initializeLiveness(def, lexicalLifetimeEnds);
322326
}
323327
};
324328

@@ -335,7 +339,8 @@ class CanonicalizeOSSALifetime final {
335339

336340
SILValue getCurrentDef() const { return currentDef; }
337341

338-
void initializeLiveness(SILValue def) {
342+
void initializeLiveness(SILValue def,
343+
ArrayRef<SILInstruction *> lexicalLifetimeEnds) {
339344
assert(consumingBlocks.empty() && debugValues.empty());
340345
// Clear the cached analysis pointer just in case the client invalidates the
341346
// analysis, freeing its memory.
@@ -344,6 +349,7 @@ class CanonicalizeOSSALifetime final {
344349
destroys.clear();
345350

346351
currentDef = def;
352+
currentLexicalLifetimeEnds = lexicalLifetimeEnds;
347353

348354
if (maximizeLifetime || respectsDeinitBarriers()) {
349355
liveness->initializeDiscoveredBlocks(&discoveredBlocks);
@@ -358,8 +364,18 @@ class CanonicalizeOSSALifetime final {
358364
}
359365

360366
/// Top-Level API: rewrites copies and destroys within \p def's extended
361-
/// lifetime. \p lifetime caches transient analysis state across multiple
362-
/// 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.
363379
///
364380
/// Return true if any change was made to \p def's extended lifetime. \p def
365381
/// itself will not be deleted and no instructions outside of \p def's
@@ -369,7 +385,8 @@ class CanonicalizeOSSALifetime final {
369385
/// This only deletes instructions within \p def's extended lifetime. Use
370386
/// InstructionDeleter::cleanUpDeadInstructions() to recursively delete dead
371387
/// operands.
372-
bool canonicalizeValueLifetime(SILValue def);
388+
bool canonicalizeValueLifetime(
389+
SILValue def, ArrayRef<SILInstruction *> lexicalLifetimeEnds = {});
373390

374391
/// Compute the liveness information for \p def. But do not do any rewriting
375392
/// or computation of boundaries.

lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -278,14 +278,29 @@ void CanonicalizeOSSALifetime::extendLivenessToDeinitBarriers() {
278278
});
279279
});
280280

281+
ArrayRef<SILInstruction *> ends = {};
282+
SmallVector<SILInstruction *, 8> lexicalEnds;
283+
if (currentLexicalLifetimeEnds.size() > 0) {
284+
visitExtendedUnconsumedBoundary(
285+
currentLexicalLifetimeEnds,
286+
[&lexicalEnds](auto *instruction, auto lifetimeEnding) {
287+
instruction->visitSubsequentInstructions([&](auto *next) {
288+
lexicalEnds.push_back(next);
289+
return true;
290+
});
291+
});
292+
ends = lexicalEnds;
293+
} else {
294+
ends = outsideDestroys;
295+
}
296+
281297
auto *def = getCurrentDef()->getDefiningInstruction();
282298
using InitialBlocks = ArrayRef<SILBasicBlock *>;
283299
auto *defBlock = getCurrentDef()->getParentBlock();
284300
auto initialBlocks = defBlock ? InitialBlocks(defBlock) : InitialBlocks();
285301
ReachableBarriers barriers;
286-
findBarriersBackward(outsideDestroys, initialBlocks,
287-
*getCurrentDef()->getFunction(), barriers,
288-
[&](auto *inst) {
302+
findBarriersBackward(ends, initialBlocks, *getCurrentDef()->getFunction(),
303+
barriers, [&](auto *inst) {
289304
if (inst == def)
290305
return true;
291306
if (!isDeinitBarrier(inst, calleeAnalysis))
@@ -569,11 +584,17 @@ void CanonicalizeOSSALifetime::findOriginalBoundary(
569584
/// extent.
570585
/// [Extend liveness down to the boundary between green blocks and uncolored.]
571586
void CanonicalizeOSSALifetime::visitExtendedUnconsumedBoundary(
572-
ArrayRef<SILInstruction *> ends,
587+
ArrayRef<SILInstruction *> consumes,
573588
llvm::function_ref<void(SILInstruction *, PrunedLiveness::LifetimeEnding)>
574589
visitor) {
575590
auto currentDef = getCurrentDef();
576591

592+
#ifndef NDEBUG
593+
for (auto *consume : consumes) {
594+
assert(!liveness->isWithinBoundary(consume));
595+
}
596+
#endif
597+
577598
// First, collect the blocks that were _originally_ live. We can't use
578599
// liveness here because it doesn't include blocks that occur before a
579600
// destroy_value.
@@ -619,7 +640,7 @@ void CanonicalizeOSSALifetime::visitExtendedUnconsumedBoundary(
619640
// consumes. These are just the instructions on the boundary which aren't
620641
// destroys.
621642
BasicBlockWorklist worklist(currentDef->getFunction());
622-
for (auto *instruction : ends) {
643+
for (auto *instruction : consumes) {
623644
if (destroys.contains(instruction))
624645
continue;
625646
if (liveness->isInterestingUser(instruction)
@@ -1223,8 +1244,9 @@ void CanonicalizeOSSALifetime::rewriteLifetimes() {
12231244
}
12241245

12251246
/// Canonicalize a single extended owned lifetime.
1226-
bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
1227-
LivenessState livenessState(*this, def);
1247+
bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(
1248+
SILValue def, ArrayRef<SILInstruction *> lexicalLifetimeEnds) {
1249+
LivenessState livenessState(*this, def, lexicalLifetimeEnds);
12281250

12291251
// Don't canonicalize the lifetimes of values of move-only type. According to
12301252
// language rules, they are fixed.
@@ -1252,6 +1274,7 @@ namespace swift::test {
12521274
// access scopes which they previously enclosed but can't be hoisted
12531275
// before
12541276
// - SILValue: value to canonicalize
1277+
// - [SILInstruction]: the lexicalLifetimeEnds to recognize
12551278
// Dumps:
12561279
// - function after value canonicalization
12571280
static FunctionTest CanonicalizeOSSALifetimeTest(
@@ -1271,7 +1294,11 @@ static FunctionTest CanonicalizeOSSALifetimeTest(
12711294
respectAccessScopes ? accessBlockAnalysis : nullptr, domTree,
12721295
calleeAnalysis, deleter);
12731296
auto value = arguments.takeValue();
1274-
canonicalizer.canonicalizeValueLifetime(value);
1297+
SmallVector<SILInstruction *, 4> lexicalLifetimeEnds;
1298+
while (arguments.hasUntaken()) {
1299+
lexicalLifetimeEnds.push_back(arguments.takeInstruction());
1300+
}
1301+
canonicalizer.canonicalizeValueLifetime(value, lexicalLifetimeEnds);
12751302
function.print(llvm::outs());
12761303
});
12771304
} // end namespace swift::test

0 commit comments

Comments
 (0)