Skip to content

[NFC] mark_dependence handling for coroutines and accessors. #77695

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 6 commits into from
Nov 19, 2024
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 @@ -45,13 +45,23 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
// Indirect results are not checked here. Type checking ensures
// that they have a lifetime dependence.
if let lifetimeDep = LifetimeDependence(argument, context) {
analyze(dependence: lifetimeDep, context)
_ = analyze(dependence: lifetimeDep, context)
}
}
for instruction in function.instructions {
if let markDep = instruction as? MarkDependenceInst, markDep.isUnresolved {
if let lifetimeDep = LifetimeDependence(markDep, context) {
analyze(dependence: lifetimeDep, context)
if analyze(dependence: lifetimeDep, context) {
// Note: This promotes the mark_dependence flag but does not invalidate SIL; preserving analyses is good,
// but the change won't appear in -sil-print-function. Ideally, we could notify context of a flag change
// without invalidating analyses.
lifetimeDep.resolve(context)
}
} else {
// For now, if the mark_dependence wasn't recognized as a lifetime dependence, conservatively settle it as
// escaping. In the future, we should not need this because, for escapable types, mark_dependence [unresolved]
// will all be settled during an early LifetimeNormalization pass.
markDep.settleToEscaping()
}
continue
}
Expand All @@ -61,7 +71,7 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
apply.resultOrYields.forEach {
if let lifetimeDep = LifetimeDependence(unsafeApplyResult: $0,
context) {
analyze(dependence: lifetimeDep, context)
_ = analyze(dependence: lifetimeDep, context)
}
}
continue
Expand All @@ -74,8 +84,9 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
/// 1. Compute the LifetimeDependence scope.
///
/// 2. Walk down all dependent values checking that they are within range.
private func analyze(dependence: LifetimeDependence,
_ context: FunctionPassContext) {
///
/// Return true on success.
private func analyze(dependence: LifetimeDependence, _ context: FunctionPassContext) -> Bool {
log("Dependence scope:\n\(dependence)")

// Compute this dependence scope.
Expand All @@ -91,10 +102,7 @@ private func analyze(dependence: LifetimeDependence,
var walker = DiagnoseDependenceWalker(diagnostics, context)
defer { walker.deinitialize() }
_ = walker.walkDown(root: dependence.dependentValue)

if !error {
dependence.resolve(context)
}
return !error
}

/// Analyze and diagnose a single LifetimeDependence.
Expand Down
6 changes: 5 additions & 1 deletion SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,10 @@ class MarkDependenceInst : SingleValueInstruction {
public func resolveToNonEscaping() {
bridged.MarkDependenceInst_resolveToNonEscaping()
}

public func settleToEscaping() {
bridged.MarkDependenceInst_settleToEscaping()
}
}

final public class RefToBridgeObjectInst : SingleValueInstruction {
Expand Down Expand Up @@ -1344,7 +1348,7 @@ final public class AbortApplyInst : Instruction, UnaryInstruction {

extension BeginApplyInst : ScopedInstruction {
public var endOperands: LazyFilterSequence<UseList> {
return token.uses.lazy.filter { $0.endsLifetime }
return token.uses.lazy.filter { $0.isScopeEndingUse }
}
}

Expand Down
13 changes: 13 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Operand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ extension Operand {
}
}

extension Operand {
/// A scope ending use is a consuming use for normal borrow scopes, but it also applies to intructions that end the
/// scope of an address (end_access) or a token (end_apply, abort_apply),
public var isScopeEndingUse: Bool {
switch instruction {
case is EndBorrowInst, is EndAccessInst, is EndApplyInst, is AbortApplyInst:
return true
default:
return false
}
}
}

extension OptionalBridgedOperand {
init(bridged: BridgedOperand?) {
self = OptionalBridgedOperand(op: bridged?.op)
Expand Down
4 changes: 3 additions & 1 deletion docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5699,6 +5699,8 @@ mark_dependence

%2 = mark_dependence %value : $*T on %base : $Builtin.NativeObject

``%base`` must not be identical to ``%value``.

Indicates that the validity of ``%value`` depends on the value of
``%base``. Operations that would destroy ``%base`` must not be moved
before any instructions which depend on the result of this
Expand All @@ -5715,7 +5717,7 @@ the dependency is on the current value stored in the address.
The optional ``nonescaping`` attribute indicates that no value derived
from ``%value`` escapes the lifetime of ``%base``. As with escaping
``mark_dependence``, all values transitively forwarded from ``%value``
must be destroyed within the lifetime of ` `%base``. Unlike escaping
must be destroyed within the lifetime of ``%base``. Unlike escaping
``mark_dependence``, this must be statically verifiable. Additionally,
unlike escaping ``mark_dependence``, derived values include copies of
``%value`` and values transitively forwarded from those copies. If
Expand Down
1 change: 1 addition & 0 deletions include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,7 @@ struct BridgedInstruction {
BRIDGED_INLINE SwiftInt AssignInst_getAssignOwnership() const;
BRIDGED_INLINE MarkDependenceKind MarkDependenceInst_dependenceKind() const;
BRIDGED_INLINE void MarkDependenceInst_resolveToNonEscaping() const;
BRIDGED_INLINE void MarkDependenceInst_settleToEscaping() const;
BRIDGED_INLINE SwiftInt BeginAccessInst_getAccessKind() const;
BRIDGED_INLINE bool BeginAccessInst_isStatic() const;
BRIDGED_INLINE bool BeginAccessInst_isUnsafe() const;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SIL/SILBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,10 @@ void BridgedInstruction::MarkDependenceInst_resolveToNonEscaping() const {
getAs<swift::MarkDependenceInst>()->resolveToNonEscaping();
}

void BridgedInstruction::MarkDependenceInst_settleToEscaping() const {
getAs<swift::MarkDependenceInst>()->settleToEscaping();
}

SwiftInt BridgedInstruction::BeginAccessInst_getAccessKind() const {
return (SwiftInt)getAs<swift::BeginAccessInst>()->getAccessKind();
}
Expand Down
3 changes: 1 addition & 2 deletions lib/AST/LifetimeDependence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,7 @@ std::optional<LifetimeDependenceInfo> LifetimeDependenceInfo::fromDependsOn(
auto kind = descriptor.getParsedLifetimeDependenceKind();

if (kind == ParsedLifetimeDependenceKind::Scope &&
(!isGuaranteedParameterInCallee(paramConvention) &&
!isMutatingParameter(paramConvention))) {
isConsumedParameterInCallee(paramConvention)) {
diags.diagnose(loc, diag::lifetime_dependence_cannot_use_kind, "_scope",
getStringForParameterConvention(paramConvention));
return true;
Expand Down