Skip to content

Commit 2fc7659

Browse files
Merge pull request swiftlang#60670 from nate-chandler/lexical_lifetimes/owned_arguments
[SIL] Maintain owned argument lifetimes at inlining.
2 parents d896877 + 7789b72 commit 2fc7659

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+469
-243
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/WalkUtils.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ extension ValueDefUseWalker {
341341
return unmatchedPath(value: operand, path: path)
342342
}
343343
case is InitExistentialRefInst, is OpenExistentialRefInst,
344-
is BeginBorrowInst, is CopyValueInst,
344+
is BeginBorrowInst, is CopyValueInst, is MoveValueInst,
345345
is UpcastInst, is UncheckedRefCastInst, is EndCOWMutationInst,
346346
is RefToBridgeObjectInst, is BridgeObjectToRefInst, is MarkMustCheckInst:
347347
return walkDownUses(ofValue: (instruction as! SingleValueInstruction), path: path)
@@ -618,7 +618,7 @@ extension ValueUseDefWalker {
618618
return rootDef(value: mvr, path: path)
619619
}
620620
case is InitExistentialRefInst, is OpenExistentialRefInst,
621-
is BeginBorrowInst, is CopyValueInst,
621+
is BeginBorrowInst, is CopyValueInst, is MoveValueInst,
622622
is UpcastInst, is UncheckedRefCastInst, is EndCOWMutationInst,
623623
is RefToBridgeObjectInst, is BridgeObjectToRefInst, is MarkMustCheckInst:
624624
return walkUp(value: (def as! Instruction).operands[0].value, path: path)

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ final public class ProjectBoxInst : SingleValueInstruction, UnaryInstruction {
609609

610610
final public class CopyValueInst : SingleValueInstruction, UnaryInstruction {}
611611

612+
final public class MoveValueInst : SingleValueInstruction, UnaryInstruction {}
613+
612614
final public class StrongCopyUnownedValueInst : SingleValueInstruction, UnaryInstruction {}
613615

614616
final public class StrongCopyUnmanagedValueInst : SingleValueInstruction, UnaryInstruction {}

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ public func registerSILClasses() {
123123
register(BeginBorrowInst.self)
124124
register(ProjectBoxInst.self)
125125
register(CopyValueInst.self)
126+
register(MoveValueInst.self)
126127
register(EndCOWMutationInst.self)
127128
register(ClassifyBridgeObjectInst.self)
128129
register(PartialApplyInst.self)

docs/SIL.rst

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,27 +2414,55 @@ Variable Lifetimes
24142414
In order for programmer intended lifetimes to be maintained under optimization,
24152415
the lifetimes of SIL values which correspond to named source-level values can
24162416
only be modified in limited ways. Generally, the behavior is that the lifetime
2417-
of a named source-level value cannot _observably_ end before the end of the
2418-
lexical scope in which that value is defined. Specifically, code motion may
2419-
not move the ends of these lifetimes across a **deinit barrier**.
2420-
2421-
A few sorts of SIL value have lifetimes that are constrained that way:
2417+
of a named source-level value is anchored to the variable's lexical scope and
2418+
confined by **deinit barriers**. Specifically, code motion may not move the
2419+
ends of these lifetimes across a deinit barrier.
2420+
2421+
Source level variables (lets, vars, ...) and function arguments will result in
2422+
SIL-level lexical lifetimes if either of the two sets of circumstances apply:
2423+
(1) Inferred lexicality.
2424+
- the type is non-trivial
2425+
- the type is not eager-move
2426+
- the variable or argument is not annotated to be eager-move
2427+
OR
2428+
(2) Explicit lexicality.
2429+
- the type, variable, or argument is annotated `@_lexical`
2430+
2431+
A type is eager-move by satisfying one of two conditions:
2432+
(1) Inferred: An aggregate is inferred to be eager-move if all of its fields are
2433+
eager-move.
2434+
(2) Annotated: Any type can be eager-move if it is annotated with an attribute
2435+
that explicitly specifies it to be: `@_eagerMove`, `@_noImplicitCopy`.
2436+
2437+
A variable or argument is eager-move by satisfying one of two conditions:
2438+
(1) Inferred: Its type is eager-move.
2439+
(2) Annotated: The variable or argument is annotated with an attribute that
2440+
specifies it to be: `@_eagerMove`, `@_noImplicitCopy`.
2441+
2442+
These source-level rules result in a few sorts of SIL value whose destroys must
2443+
not be moved across deinit barriers:
24222444

24232445
1: `begin_borrow [lexical]`
24242446
2: `move_value [lexical]`
2425-
3: @owned function arguments
2447+
3: function arguments
24262448
4: `alloc_stack [lexical]`
24272449

2428-
That these three have constrained lifetimes is encoded in ValueBase::isLexical,
2429-
which should be checked before changing the lifetime of a value.
2450+
To translate from the source-level representation of lexicality to the
2451+
SIL-level representation, for source-level variables (vars, lets, ...) SILGen
2452+
generates `begin_borrow [lexical]`, `move_value [lexical]`, `alloc_stack
2453+
[lexical]` . For function arguments, there is no work to do:
2454+
a `SILFunctionArgument` itself can be lexical
2455+
(`SILFunctionArgument::isLexical`).
2456+
2457+
That the first three have constrained lifetimes is encoded in
2458+
ValueBase::isLexical, which should be checked before changing the lifetime of a
2459+
value.
24302460

2431-
The reason that only @owned function arguments are constrained is that a
2432-
@guaranteed function argument is guaranteed by the function's caller to live for
2433-
the full duration of the function already. Optimization of the function alone
2434-
can't shorten it. When such a function is inlined into its caller, though, a
2435-
lexical borrow scope is added for each of its @guaranteed arguments, ensuring
2436-
that the lifetime of the corresponding source-level value is not shortened in a
2437-
way that doesn't respect deinit barriers.
2461+
When a function is inlined into its caller, a lexical borrow scope is added for
2462+
each of its @guaranteed arguments, and a lexical move is added for each of its
2463+
@owned arguments, (unless the values being passed are already lexical
2464+
themselves) ensuring that the lifetimes of the corresponding source-level
2465+
values are not shortened in a way that doesn't respect deinit barriers.
24382466

24392467
Unlike the other sorts, `alloc_stack [lexical]` isn't a SILValue. Instead, it
24402468
constrains the lifetime of an addressable variable. Since the constraint is

include/swift/SIL/InstructionUtils.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ SILValue stripBorrow(SILValue V);
8888
//===----------------------------------------------------------------------===//
8989

9090
/// Return a non-null SingleValueInstruction if the given instruction merely
91-
/// copies the value of its first operand, possibly changing its type or
92-
/// ownership state, but otherwise having no effect.
91+
/// copies or moves the value of its first operand, possibly changing its type
92+
/// or ownership state, but otherwise having no effect.
9393
///
9494
/// The returned instruction may have additional "incidental" operands;
9595
/// mark_dependence for example.

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109

110110
namespace swift {
111111

112-
extern llvm::Statistic NumCopiesEliminated;
112+
extern llvm::Statistic NumCopiesAndMovesEliminated;
113113
extern llvm::Statistic NumCopiesGenerated;
114114

115115
/// Insert a copy on this operand. Trace and update stats.

lib/SIL/IR/SILValue.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,8 @@ ValueBase::getDefiningInstructionResult() {
116116
}
117117

118118
bool ValueBase::isLexical() const {
119-
if (auto *argument = dyn_cast<SILFunctionArgument>(this)) {
120-
// TODO: Recognize guaranteed arguments as lexical too.
121-
return argument->getOwnershipKind() == OwnershipKind::Owned &&
122-
argument->getLifetime().isLexical();
123-
}
119+
if (auto *argument = dyn_cast<SILFunctionArgument>(this))
120+
return argument->getLifetime().isLexical();
124121
if (auto *bbi = dyn_cast<BeginBorrowInst>(this))
125122
return bbi->isLexical();
126123
if (auto *mvi = dyn_cast<MoveValueInst>(this))

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ SILValue swift::lookThroughOwnershipInsts(SILValue v) {
3131
switch (v->getKind()) {
3232
default:
3333
return v;
34+
case ValueKind::MoveValueInst:
3435
case ValueKind::CopyValueInst:
3536
case ValueKind::BeginBorrowInst:
3637
v = cast<SingleValueInstruction>(v)->getOperand(0);
@@ -255,6 +256,7 @@ SingleValueInstruction *swift::getSingleValueCopyOrCast(SILInstruction *I) {
255256
case SILInstructionKind::BeginBorrowInst:
256257
case SILInstructionKind::BeginAccessInst:
257258
case SILInstructionKind::MarkDependenceInst:
259+
case SILInstructionKind::MoveValueInst:
258260
return cast<SingleValueInstruction>(I);
259261
}
260262
}

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,16 +2414,25 @@ void swift::visitTransitiveEndBorrows(
24142414
///
24152415
/// A begin_borrow [lexical] is nested if the borrowed value's lifetime is
24162416
/// guaranteed by another lexical scope. That happens if:
2417-
/// - the value is a guaranteed argument to the function
2418-
/// - the value is itself a begin_borrow [lexical]
2417+
/// - the non-guaranteed borrowee's value is lexical
2418+
/// - the guaranteed borrowee's value's reference roots are lexical
2419+
/// - for example, the borrowee is itself a begin_borrow [lexical]
24192420
bool swift::isNestedLexicalBeginBorrow(BeginBorrowInst *bbi) {
24202421
assert(bbi->isLexical());
24212422
auto value = bbi->getOperand();
2422-
if (auto *outerBBI = dyn_cast<BeginBorrowInst>(value)) {
2423-
return outerBBI->isLexical();
2423+
if (value->getOwnershipKind() != OwnershipKind::Guaranteed) {
2424+
return value->isLexical();
24242425
}
2425-
if (auto *arg = dyn_cast<SILFunctionArgument>(value)) {
2426-
return arg->getOwnershipKind() == OwnershipKind::Guaranteed;
2427-
}
2428-
return false;
2426+
SmallVector<SILValue, 8> roots;
2427+
findGuaranteedReferenceRoots(value, /*lookThroughNestedBorrows=*/false,
2428+
roots);
2429+
return llvm::all_of(roots, [](auto root) {
2430+
if (auto *outerBBI = dyn_cast<BeginBorrowInst>(root)) {
2431+
return outerBBI->isLexical();
2432+
}
2433+
if (auto *arg = dyn_cast<SILFunctionArgument>(root)) {
2434+
return arg->getOwnershipKind() == OwnershipKind::Guaranteed;
2435+
}
2436+
return false;
2437+
});
24292438
}

lib/SIL/Utils/Projection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ Optional<ProjectionPath> ProjectionPath::getProjectionPath(SILValue Start,
387387
// TODO: migrate users to getProjectionPath to the AccessPath utility to
388388
// avoid this hack.
389389
if (!isa<EndCOWMutationInst>(Iter) && !isa<BeginAccessInst>(Iter) &&
390-
!isa<BeginBorrowInst>(Iter)) {
390+
!isa<BeginBorrowInst>(Iter) && !isa<MoveValueInst>(Iter)) {
391391
Projection AP(Iter);
392392
if (!AP.isValid())
393393
break;

0 commit comments

Comments
 (0)