Skip to content

[LifetimeCompletion] Mark lifetimes in dead-end loops. #73859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ func computeLinearLiveness(for definingValue: Value, _ context: Context)
var range = InstructionRange(for: definingValue, context)

// Compute liveness.
definingValue.lookThroughBorrowedFromUser.uses.endingLifetime.forEach {
range.insert($0.instruction)
for use in definingValue.lookThroughBorrowedFromUser.uses {
let instruction = use.instruction
if use.endsLifetime || instruction is ExtendLifetimeInst {
range.insert(instruction)
}
}
return range
}
Expand Down
2 changes: 2 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,8 @@ final public class DestroyAddrInst : Instruction, UnaryInstruction {

final public class EndLifetimeInst : Instruction, UnaryInstruction {}

final public class ExtendLifetimeInst : Instruction, UnaryInstruction {}

final public class InjectEnumAddrInst : Instruction, UnaryInstruction, EnumInstruction {
public var `enum`: Value { operand.value }
public var caseIndex: Int { bridged.InjectEnumAddrInst_caseIndex() }
Expand Down
1 change: 1 addition & 0 deletions SwiftCompilerSources/Sources/SIL/Registration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public func registerSILClasses() {
register(DestroyValueInst.self)
register(DestroyAddrInst.self)
register(EndLifetimeInst.self)
register(ExtendLifetimeInst.self)
register(StrongCopyUnownedValueInst.self)
register(StrongCopyUnmanagedValueInst.self)
register(StrongCopyWeakValueInst.self)
Expand Down
16 changes: 16 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4682,6 +4682,22 @@ The instruction accepts an object or address type.
This instruction is valid only in OSSA and is lowered to a no-op when lowering
to non-OSSA.

extend_lifetime
```````````````

::

sil-instruction ::= 'extend_lifetime' sil-operand

// Indicate that %0's linear lifetime extends to this point
extend_lifetime %0 : $X

Indicates that a value's linear lifetime extends to this point. Inserted by
OSSALifetimeCompletion(AvailabilityBoundary) in order to provide the invariant
that a value is either consumed OR has an `extend_lifetime` user on all paths
and furthermore that all uses are within the boundary defined by that set of
instructions (the consumes and the `extend_lifetime`s).

assign
``````
::
Expand Down
11 changes: 9 additions & 2 deletions include/swift/SIL/OSSALifetimeCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,17 @@ class OSSALifetimeCompletion {
DoNotAllowLeaks = false,
};

static void visitUnreachableLifetimeEnds(
enum class LifetimeEnd : uint8_t {
/// The lifetime ends at the boundary.
Boundary,
/// The lifetime "ends" in a loop.
Loop,
};

static void visitAvailabilityBoundary(
SILValue value, AllowLeaks_t allowLeaks,
const SSAPrunedLiveness &liveness,
llvm::function_ref<void(SILInstruction *)> visit);
llvm::function_ref<void(SILInstruction *, LifetimeEnd end)> visit);

protected:
bool analyzeAndUpdateLifetime(SILValue value, Boundary boundary);
Expand Down
12 changes: 11 additions & 1 deletion include/swift/SIL/OwnershipLiveness.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,19 @@ class LinearLiveness : public OSSALiveness {
friend LinearLivenessVisitor;

public:
LinearLiveness(SILValue def);
/// Whether extend_lifetime instructions should be added to the boundary.
/// Used to verify extend_lifetime instructions.
enum IncludeExtensions_t {
DoNotIncludeExtensions = false,
IncludeExtensions = true,
};
LinearLiveness(SILValue def,
IncludeExtensions_t includeExtensions = IncludeExtensions);

void compute();

private:
const IncludeExtensions_t includeExtensions;
};

// Internal implementation
Expand Down
10 changes: 10 additions & 0 deletions include/swift/SIL/OwnershipUseVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ bool OwnershipUseVisitor<Impl>::visitLifetimeEndingUses(SILValue ssaDef) {
template <typename Impl>
bool OwnershipUseVisitor<Impl>::visitConsumes(SILValue ssaDef) {
for (Operand *use : ssaDef->getUses()) {
// extend_lifetime instructions are non-consuming but need to be visited
// because together with consuming uses they enclose all users of the value.
if (isa<ExtendLifetimeInst>(use->getUser())) {
if (!handleUsePoint(use, UseLifetimeConstraint::NonLifetimeEnding))
return false;
continue;
}
if (use->isConsuming()) {
if (PhiOperand(use) && !asImpl().handleOwnedPhi(use))
return false;
Expand Down Expand Up @@ -245,6 +252,9 @@ bool OwnershipUseVisitor<Impl>::visitOuterBorrowScopeEnd(Operand *borrowEnd) {

return handleUsePoint(borrowEnd, UseLifetimeConstraint::LifetimeEnding);

case OperandOwnership::InstantaneousUse:
assert(isa<ExtendLifetimeInst>(borrowEnd->getUser()));
return handleUsePoint(borrowEnd, UseLifetimeConstraint::NonLifetimeEnding);
default:
llvm_unreachable("expected borrow scope end");
}
Expand Down
20 changes: 9 additions & 11 deletions include/swift/SIL/PrunedLiveness.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,22 +391,20 @@ class PrunedLiveness {
Ending,
// The instruction doesn't use the value.
NonUse,
};
Value value;
} value;

LifetimeEnding(Value value) : value(value) {}
explicit LifetimeEnding(bool lifetimeEnding)
: value(lifetimeEnding ? Value::Ending : Value::NonEnding) {}
operator Value() const { return value; }

static LifetimeEnding forUse(bool lifetimeEnding) {
return lifetimeEnding ? Value::Ending : Value::NonEnding;
}
bool isEnding() const { return value == Value::Ending; }

LifetimeEnding meet(LifetimeEnding const other) const {
return value < other.value ? *this : other;
}
void meetInPlace(LifetimeEnding const other) { *this = meet(other); }
bool isEnding() const { return value == Value::Ending; }

static LifetimeEnding NonUse() { return {Value::NonUse}; };
static LifetimeEnding Ending() { return {Value::Ending}; };
static LifetimeEnding NonEnding() { return {Value::NonEnding}; };
};

protected:
Expand Down Expand Up @@ -467,8 +465,8 @@ class PrunedLiveness {
auto useIter = users.find(user);
if (useIter == users.end())
return NonUser;
return useIter->second == LifetimeEnding::Ending() ? LifetimeEndingUse
: NonLifetimeEndingUse;
return useIter->second.isEnding() ? LifetimeEndingUse
: NonLifetimeEndingUse;
}

using ConstUserRange =
Expand Down
5 changes: 5 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,11 @@ class SILBuilder {
EndLifetimeInst(getSILDebugLocation(Loc), Operand));
}

ExtendLifetimeInst *createExtendLifetime(SILLocation Loc, SILValue Operand) {
return insert(new (getModule())
ExtendLifetimeInst(getSILDebugLocation(Loc), Operand));
}

UncheckedOwnershipConversionInst *
createUncheckedOwnershipConversion(SILLocation Loc, SILValue Operand,
ValueOwnershipKind Kind) {
Expand Down
12 changes: 12 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -3019,6 +3019,18 @@ void SILCloner<ImplClass>::visitEndLifetimeInst(EndLifetimeInst *Inst) {
getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitExtendLifetimeInst(ExtendLifetimeInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));

if (!getBuilder().hasOwnership())
return;

recordClonedInstruction(
Inst, getBuilder().createExtendLifetime(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitUncheckedOwnershipConversionInst(
UncheckedOwnershipConversionInst *Inst) {
Expand Down
10 changes: 10 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -8535,6 +8535,16 @@ class EndLifetimeInst
: UnaryInstructionBase(DebugLoc, Operand) {}
};

/// Mark the end of the linear live range of a value without destroying it.
class ExtendLifetimeInst
: public UnaryInstructionBase<SILInstructionKind::ExtendLifetimeInst,
NonValueInstruction> {
friend SILBuilder;

ExtendLifetimeInst(SILDebugLocation loc, SILValue operand)
: UnaryInstructionBase(loc, operand) {}
};

/// An unsafe conversion in between ownership kinds.
///
/// This is used today in destructors where due to Objective-C legacy
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SIL/SILNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,8 @@ NON_VALUE_INST(DestroyAddrInst, destroy_addr,
SILInstruction, MayHaveSideEffects, MayRelease)
NON_VALUE_INST(EndLifetimeInst, end_lifetime,
SILInstruction, MayHaveSideEffects, MayRelease)
NON_VALUE_INST(ExtendLifetimeInst, extend_lifetime,
SILInstruction, None, DoesNotRelease)
NON_VALUE_INST(InjectEnumAddrInst, inject_enum_addr,
SILInstruction, MayWrite, DoesNotRelease)
NON_VALUE_INST(DeinitExistentialAddrInst, deinit_existential_addr,
Expand Down
3 changes: 3 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,9 @@ class IRGenSILFunction :
void visitEndLifetimeInst(EndLifetimeInst *i) {
llvm_unreachable("unimplemented");
}
void visitExtendLifetimeInst(ExtendLifetimeInst *i) {
llvm_unreachable("should not exist after ownership lowering!?");
}
void
visitUncheckedOwnershipConversionInst(UncheckedOwnershipConversionInst *i) {
llvm_unreachable("unimplemented");
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ OPERAND_OWNERSHIP(InstantaneousUse, SuperMethod)
OPERAND_OWNERSHIP(InstantaneousUse, ClassifyBridgeObject)
OPERAND_OWNERSHIP(InstantaneousUse, UnownedCopyValue)
OPERAND_OWNERSHIP(InstantaneousUse, WeakCopyValue)
OPERAND_OWNERSHIP(InstantaneousUse, ExtendLifetime)
#define REF_STORAGE(Name, ...) \
OPERAND_OWNERSHIP(InstantaneousUse, StrongCopy##Name##Value)
#include "swift/AST/ReferenceStorage.def"
Expand Down
3 changes: 3 additions & 0 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2565,6 +2565,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
void visitEndLifetimeInst(EndLifetimeInst *ELI) {
*this << getIDAndType(ELI->getOperand());
}
void visitExtendLifetimeInst(ExtendLifetimeInst *ELLI) {
*this << getIDAndType(ELLI->getOperand());
}
void visitValueToBridgeObjectInst(ValueToBridgeObjectInst *VBOI) {
*this << getIDAndType(VBOI->getOperand());
}
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3308,6 +3308,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
UNARY_INSTRUCTION(ValueToBridgeObject)
UNARY_INSTRUCTION(FixLifetime)
UNARY_INSTRUCTION(EndLifetime)
UNARY_INSTRUCTION(ExtendLifetime)
UNARY_INSTRUCTION(CopyBlock)
UNARY_INSTRUCTION(IsUnique)
UNARY_INSTRUCTION(DestroyAddr)
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Utils/InstructionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
case SILInstructionKind::AssignOrInitInst:
case SILInstructionKind::MarkFunctionEscapeInst:
case SILInstructionKind::EndLifetimeInst:
case SILInstructionKind::ExtendLifetimeInst:
case SILInstructionKind::EndApplyInst:
case SILInstructionKind::AbortApplyInst:
case SILInstructionKind::CondFailInst:
Expand Down
Loading