Skip to content

Commit 40e8b15

Browse files
Merge pull request #73859 from nate-chandler/lifetime-completion/20240523/1
[LifetimeCompletion] Mark lifetimes in dead-end loops.
2 parents 5aa63fc + 84da0ed commit 40e8b15

38 files changed

+1233
-90
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,11 @@ func computeLinearLiveness(for definingValue: Value, _ context: Context)
6464
var range = InstructionRange(for: definingValue, context)
6565

6666
// Compute liveness.
67-
definingValue.lookThroughBorrowedFromUser.uses.endingLifetime.forEach {
68-
range.insert($0.instruction)
67+
for use in definingValue.lookThroughBorrowedFromUser.uses {
68+
let instruction = use.instruction
69+
if use.endsLifetime || instruction is ExtendLifetimeInst {
70+
range.insert(instruction)
71+
}
6972
}
7073
return range
7174
}

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,8 @@ final public class DestroyAddrInst : Instruction, UnaryInstruction {
514514

515515
final public class EndLifetimeInst : Instruction, UnaryInstruction {}
516516

517+
final public class ExtendLifetimeInst : Instruction, UnaryInstruction {}
518+
517519
final public class InjectEnumAddrInst : Instruction, UnaryInstruction, EnumInstruction {
518520
public var `enum`: Value { operand.value }
519521
public var caseIndex: Int { bridged.InjectEnumAddrInst_caseIndex() }

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public func registerSILClasses() {
7878
register(DestroyValueInst.self)
7979
register(DestroyAddrInst.self)
8080
register(EndLifetimeInst.self)
81+
register(ExtendLifetimeInst.self)
8182
register(StrongCopyUnownedValueInst.self)
8283
register(StrongCopyUnmanagedValueInst.self)
8384
register(StrongCopyWeakValueInst.self)

docs/SIL.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4682,6 +4682,22 @@ The instruction accepts an object or address type.
46824682
This instruction is valid only in OSSA and is lowered to a no-op when lowering
46834683
to non-OSSA.
46844684

4685+
extend_lifetime
4686+
```````````````
4687+
4688+
::
4689+
4690+
sil-instruction ::= 'extend_lifetime' sil-operand
4691+
4692+
// Indicate that %0's linear lifetime extends to this point
4693+
extend_lifetime %0 : $X
4694+
4695+
Indicates that a value's linear lifetime extends to this point. Inserted by
4696+
OSSALifetimeCompletion(AvailabilityBoundary) in order to provide the invariant
4697+
that a value is either consumed OR has an `extend_lifetime` user on all paths
4698+
and furthermore that all uses are within the boundary defined by that set of
4699+
instructions (the consumes and the `extend_lifetime`s).
4700+
46854701
assign
46864702
``````
46874703
::

include/swift/SIL/OSSALifetimeCompletion.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,17 @@ class OSSALifetimeCompletion {
120120
DoNotAllowLeaks = false,
121121
};
122122

123-
static void visitUnreachableLifetimeEnds(
123+
enum class LifetimeEnd : uint8_t {
124+
/// The lifetime ends at the boundary.
125+
Boundary,
126+
/// The lifetime "ends" in a loop.
127+
Loop,
128+
};
129+
130+
static void visitAvailabilityBoundary(
124131
SILValue value, AllowLeaks_t allowLeaks,
125132
const SSAPrunedLiveness &liveness,
126-
llvm::function_ref<void(SILInstruction *)> visit);
133+
llvm::function_ref<void(SILInstruction *, LifetimeEnd end)> visit);
127134

128135
protected:
129136
bool analyzeAndUpdateLifetime(SILValue value, Boundary boundary);

include/swift/SIL/OwnershipLiveness.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,19 @@ class LinearLiveness : public OSSALiveness {
206206
friend LinearLivenessVisitor;
207207

208208
public:
209-
LinearLiveness(SILValue def);
209+
/// Whether extend_lifetime instructions should be added to the boundary.
210+
/// Used to verify extend_lifetime instructions.
211+
enum IncludeExtensions_t {
212+
DoNotIncludeExtensions = false,
213+
IncludeExtensions = true,
214+
};
215+
LinearLiveness(SILValue def,
216+
IncludeExtensions_t includeExtensions = IncludeExtensions);
210217

211218
void compute();
219+
220+
private:
221+
const IncludeExtensions_t includeExtensions;
212222
};
213223

214224
// Internal implementation

include/swift/SIL/OwnershipUseVisitor.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,13 @@ bool OwnershipUseVisitor<Impl>::visitLifetimeEndingUses(SILValue ssaDef) {
213213
template <typename Impl>
214214
bool OwnershipUseVisitor<Impl>::visitConsumes(SILValue ssaDef) {
215215
for (Operand *use : ssaDef->getUses()) {
216+
// extend_lifetime instructions are non-consuming but need to be visited
217+
// because together with consuming uses they enclose all users of the value.
218+
if (isa<ExtendLifetimeInst>(use->getUser())) {
219+
if (!handleUsePoint(use, UseLifetimeConstraint::NonLifetimeEnding))
220+
return false;
221+
continue;
222+
}
216223
if (use->isConsuming()) {
217224
if (PhiOperand(use) && !asImpl().handleOwnedPhi(use))
218225
return false;
@@ -245,6 +252,9 @@ bool OwnershipUseVisitor<Impl>::visitOuterBorrowScopeEnd(Operand *borrowEnd) {
245252

246253
return handleUsePoint(borrowEnd, UseLifetimeConstraint::LifetimeEnding);
247254

255+
case OperandOwnership::InstantaneousUse:
256+
assert(isa<ExtendLifetimeInst>(borrowEnd->getUser()));
257+
return handleUsePoint(borrowEnd, UseLifetimeConstraint::NonLifetimeEnding);
248258
default:
249259
llvm_unreachable("expected borrow scope end");
250260
}

include/swift/SIL/PrunedLiveness.h

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -391,22 +391,20 @@ class PrunedLiveness {
391391
Ending,
392392
// The instruction doesn't use the value.
393393
NonUse,
394-
};
395-
Value value;
394+
} value;
396395

397396
LifetimeEnding(Value value) : value(value) {}
398-
explicit LifetimeEnding(bool lifetimeEnding)
399-
: value(lifetimeEnding ? Value::Ending : Value::NonEnding) {}
400397
operator Value() const { return value; }
398+
399+
static LifetimeEnding forUse(bool lifetimeEnding) {
400+
return lifetimeEnding ? Value::Ending : Value::NonEnding;
401+
}
402+
bool isEnding() const { return value == Value::Ending; }
403+
401404
LifetimeEnding meet(LifetimeEnding const other) const {
402405
return value < other.value ? *this : other;
403406
}
404407
void meetInPlace(LifetimeEnding const other) { *this = meet(other); }
405-
bool isEnding() const { return value == Value::Ending; }
406-
407-
static LifetimeEnding NonUse() { return {Value::NonUse}; };
408-
static LifetimeEnding Ending() { return {Value::Ending}; };
409-
static LifetimeEnding NonEnding() { return {Value::NonEnding}; };
410408
};
411409

412410
protected:
@@ -467,8 +465,8 @@ class PrunedLiveness {
467465
auto useIter = users.find(user);
468466
if (useIter == users.end())
469467
return NonUser;
470-
return useIter->second == LifetimeEnding::Ending() ? LifetimeEndingUse
471-
: NonLifetimeEndingUse;
468+
return useIter->second.isEnding() ? LifetimeEndingUse
469+
: NonLifetimeEndingUse;
472470
}
473471

474472
using ConstUserRange =

include/swift/SIL/SILBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2292,6 +2292,11 @@ class SILBuilder {
22922292
EndLifetimeInst(getSILDebugLocation(Loc), Operand));
22932293
}
22942294

2295+
ExtendLifetimeInst *createExtendLifetime(SILLocation Loc, SILValue Operand) {
2296+
return insert(new (getModule())
2297+
ExtendLifetimeInst(getSILDebugLocation(Loc), Operand));
2298+
}
2299+
22952300
UncheckedOwnershipConversionInst *
22962301
createUncheckedOwnershipConversion(SILLocation Loc, SILValue Operand,
22972302
ValueOwnershipKind Kind) {

include/swift/SIL/SILCloner.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3019,6 +3019,18 @@ void SILCloner<ImplClass>::visitEndLifetimeInst(EndLifetimeInst *Inst) {
30193019
getOpValue(Inst->getOperand())));
30203020
}
30213021

3022+
template <typename ImplClass>
3023+
void SILCloner<ImplClass>::visitExtendLifetimeInst(ExtendLifetimeInst *Inst) {
3024+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
3025+
3026+
if (!getBuilder().hasOwnership())
3027+
return;
3028+
3029+
recordClonedInstruction(
3030+
Inst, getBuilder().createExtendLifetime(getOpLocation(Inst->getLoc()),
3031+
getOpValue(Inst->getOperand())));
3032+
}
3033+
30223034
template <typename ImplClass>
30233035
void SILCloner<ImplClass>::visitUncheckedOwnershipConversionInst(
30243036
UncheckedOwnershipConversionInst *Inst) {

include/swift/SIL/SILInstruction.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8535,6 +8535,16 @@ class EndLifetimeInst
85358535
: UnaryInstructionBase(DebugLoc, Operand) {}
85368536
};
85378537

8538+
/// Mark the end of the linear live range of a value without destroying it.
8539+
class ExtendLifetimeInst
8540+
: public UnaryInstructionBase<SILInstructionKind::ExtendLifetimeInst,
8541+
NonValueInstruction> {
8542+
friend SILBuilder;
8543+
8544+
ExtendLifetimeInst(SILDebugLocation loc, SILValue operand)
8545+
: UnaryInstructionBase(loc, operand) {}
8546+
};
8547+
85388548
/// An unsafe conversion in between ownership kinds.
85398549
///
85408550
/// This is used today in destructors where due to Objective-C legacy

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,8 @@ NON_VALUE_INST(DestroyAddrInst, destroy_addr,
864864
SILInstruction, MayHaveSideEffects, MayRelease)
865865
NON_VALUE_INST(EndLifetimeInst, end_lifetime,
866866
SILInstruction, MayHaveSideEffects, MayRelease)
867+
NON_VALUE_INST(ExtendLifetimeInst, extend_lifetime,
868+
SILInstruction, None, DoesNotRelease)
867869
NON_VALUE_INST(InjectEnumAddrInst, inject_enum_addr,
868870
SILInstruction, MayWrite, DoesNotRelease)
869871
NON_VALUE_INST(DeinitExistentialAddrInst, deinit_existential_addr,

lib/IRGen/IRGenSIL.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,9 @@ class IRGenSILFunction :
13501350
void visitEndLifetimeInst(EndLifetimeInst *i) {
13511351
llvm_unreachable("unimplemented");
13521352
}
1353+
void visitExtendLifetimeInst(ExtendLifetimeInst *i) {
1354+
llvm_unreachable("should not exist after ownership lowering!?");
1355+
}
13531356
void
13541357
visitUncheckedOwnershipConversionInst(UncheckedOwnershipConversionInst *i) {
13551358
llvm_unreachable("unimplemented");

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ OPERAND_OWNERSHIP(InstantaneousUse, SuperMethod)
225225
OPERAND_OWNERSHIP(InstantaneousUse, ClassifyBridgeObject)
226226
OPERAND_OWNERSHIP(InstantaneousUse, UnownedCopyValue)
227227
OPERAND_OWNERSHIP(InstantaneousUse, WeakCopyValue)
228+
OPERAND_OWNERSHIP(InstantaneousUse, ExtendLifetime)
228229
#define REF_STORAGE(Name, ...) \
229230
OPERAND_OWNERSHIP(InstantaneousUse, StrongCopy##Name##Value)
230231
#include "swift/AST/ReferenceStorage.def"

lib/SIL/IR/SILPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,6 +2565,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
25652565
void visitEndLifetimeInst(EndLifetimeInst *ELI) {
25662566
*this << getIDAndType(ELI->getOperand());
25672567
}
2568+
void visitExtendLifetimeInst(ExtendLifetimeInst *ELLI) {
2569+
*this << getIDAndType(ELLI->getOperand());
2570+
}
25682571
void visitValueToBridgeObjectInst(ValueToBridgeObjectInst *VBOI) {
25692572
*this << getIDAndType(VBOI->getOperand());
25702573
}

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3308,6 +3308,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
33083308
UNARY_INSTRUCTION(ValueToBridgeObject)
33093309
UNARY_INSTRUCTION(FixLifetime)
33103310
UNARY_INSTRUCTION(EndLifetime)
3311+
UNARY_INSTRUCTION(ExtendLifetime)
33113312
UNARY_INSTRUCTION(CopyBlock)
33123313
UNARY_INSTRUCTION(IsUnique)
33133314
UNARY_INSTRUCTION(DestroyAddr)

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
560560
case SILInstructionKind::AssignOrInitInst:
561561
case SILInstructionKind::MarkFunctionEscapeInst:
562562
case SILInstructionKind::EndLifetimeInst:
563+
case SILInstructionKind::ExtendLifetimeInst:
563564
case SILInstructionKind::EndApplyInst:
564565
case SILInstructionKind::AbortApplyInst:
565566
case SILInstructionKind::CondFailInst:

0 commit comments

Comments
 (0)