Skip to content

Commit 04ffbe8

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 dc9baba commit 04ffbe8

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
@@ -283,14 +283,29 @@ void CanonicalizeOSSALifetime::extendLivenessToDeinitBarriers() {
283283
});
284284
});
285285

286+
ArrayRef<SILInstruction *> ends = {};
287+
SmallVector<SILInstruction *, 8> lexicalEnds;
288+
if (currentLexicalLifetimeEnds.size() > 0) {
289+
visitExtendedUnconsumedBoundary(
290+
currentLexicalLifetimeEnds,
291+
[&lexicalEnds](auto *instruction, auto lifetimeEnding) {
292+
instruction->visitSubsequentInstructions([&](auto *next) {
293+
lexicalEnds.push_back(next);
294+
return true;
295+
});
296+
});
297+
ends = lexicalEnds;
298+
} else {
299+
ends = outsideDestroys;
300+
}
301+
286302
auto *def = getCurrentDef()->getDefiningInstruction();
287303
using InitialBlocks = ArrayRef<SILBasicBlock *>;
288304
auto *defBlock = getCurrentDef()->getParentBlock();
289305
auto initialBlocks = defBlock ? InitialBlocks(defBlock) : InitialBlocks();
290306
ReachableBarriers barriers;
291-
findBarriersBackward(outsideDestroys, initialBlocks,
292-
*getCurrentDef()->getFunction(), barriers,
293-
[&](auto *inst) {
307+
findBarriersBackward(ends, initialBlocks, *getCurrentDef()->getFunction(),
308+
barriers, [&](auto *inst) {
294309
if (inst == def)
295310
return true;
296311
if (!isDeinitBarrier(inst, calleeAnalysis))
@@ -574,11 +589,17 @@ void CanonicalizeOSSALifetime::findOriginalBoundary(
574589
/// extent.
575590
/// [Extend liveness down to the boundary between green blocks and uncolored.]
576591
void CanonicalizeOSSALifetime::visitExtendedUnconsumedBoundary(
577-
ArrayRef<SILInstruction *> ends,
592+
ArrayRef<SILInstruction *> consumes,
578593
llvm::function_ref<void(SILInstruction *, PrunedLiveness::LifetimeEnding)>
579594
visitor) {
580595
auto currentDef = getCurrentDef();
581596

597+
#ifndef NDEBUG
598+
for (auto *consume : consumes) {
599+
assert(!liveness->isWithinBoundary(consume));
600+
}
601+
#endif
602+
582603
// First, collect the blocks that were _originally_ live. We can't use
583604
// liveness here because it doesn't include blocks that occur before a
584605
// destroy_value.
@@ -624,7 +645,7 @@ void CanonicalizeOSSALifetime::visitExtendedUnconsumedBoundary(
624645
// consumes. These are just the instructions on the boundary which aren't
625646
// destroys.
626647
BasicBlockWorklist worklist(currentDef->getFunction());
627-
for (auto *instruction : ends) {
648+
for (auto *instruction : consumes) {
628649
if (destroys.contains(instruction))
629650
continue;
630651
if (liveness->isInterestingUser(instruction)
@@ -1228,8 +1249,9 @@ void CanonicalizeOSSALifetime::rewriteLifetimes() {
12281249
}
12291250

12301251
/// Canonicalize a single extended owned lifetime.
1231-
bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
1232-
LivenessState livenessState(*this, def);
1252+
bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(
1253+
SILValue def, ArrayRef<SILInstruction *> lexicalLifetimeEnds) {
1254+
LivenessState livenessState(*this, def, lexicalLifetimeEnds);
12331255

12341256
// Don't canonicalize the lifetimes of values of move-only type. According to
12351257
// language rules, they are fixed.
@@ -1257,6 +1279,7 @@ namespace swift::test {
12571279
// access scopes which they previously enclosed but can't be hoisted
12581280
// before
12591281
// - SILValue: value to canonicalize
1282+
// - [SILInstruction]: the lexicalLifetimeEnds to recognize
12601283
// Dumps:
12611284
// - function after value canonicalization
12621285
static FunctionTest CanonicalizeOSSALifetimeTest(
@@ -1276,7 +1299,11 @@ static FunctionTest CanonicalizeOSSALifetimeTest(
12761299
respectAccessScopes ? accessBlockAnalysis : nullptr, domTree,
12771300
calleeAnalysis, deleter);
12781301
auto value = arguments.takeValue();
1279-
canonicalizer.canonicalizeValueLifetime(value);
1302+
SmallVector<SILInstruction *, 4> lexicalLifetimeEnds;
1303+
while (arguments.hasUntaken()) {
1304+
lexicalLifetimeEnds.push_back(arguments.takeInstruction());
1305+
}
1306+
canonicalizer.canonicalizeValueLifetime(value, lexicalLifetimeEnds);
12801307
function.print(llvm::outs());
12811308
});
12821309
} // end namespace swift::test

0 commit comments

Comments
 (0)