Skip to content

Commit 1d1b260

Browse files
authored
Merge pull request #78199 from atrick/lifedep-cleanup
LifetimeDependence: minor diagnostic quality fixes and related utilities
2 parents b7f0105 + c4a2200 commit 1d1b260

File tree

13 files changed

+201
-34
lines changed

13 files changed

+201
-34
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
///
13+
/// Pass dependencies:
14+
///
15+
/// - After MoveOnly checking fixes non-Copyable lifetimes.
16+
///
17+
/// - Before MoveOnlyTypeEliminator removes ownership operations on trivial types, which loses variable information
18+
/// required for diagnostics.
19+
///
20+
//===----------------------------------------------------------------------===//
1221

1322
import AST
1423
import SIL
@@ -250,22 +259,6 @@ private struct DiagnoseDependence {
250259
}
251260
}
252261

253-
private extension Instruction {
254-
func findVarDecl() -> VarDecl? {
255-
if let varDeclInst = self as? VarDeclInstruction {
256-
return varDeclInst.varDecl
257-
}
258-
for result in results {
259-
for use in result.uses {
260-
if let debugVal = use.instruction as? DebugValueInst {
261-
return debugVal.varDecl
262-
}
263-
}
264-
}
265-
return nil
266-
}
267-
}
268-
269262
// Identify a best-effort variable declaration based on a defining SIL
270263
// value or any lifetime dependent use of that SIL value.
271264
private struct LifetimeVariable {
@@ -327,7 +320,7 @@ private struct LifetimeVariable {
327320
self = Self(introducer: allocStack)
328321
case .global(let globalVar):
329322
self.varDecl = globalVar.varDecl
330-
self.sourceLoc = nil
323+
self.sourceLoc = varDecl?.nameLoc
331324
case .class(let refAddr):
332325
self.varDecl = refAddr.varDecl
333326
self.sourceLoc = refAddr.location.sourceLoc

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,22 @@ extension AddressOwnershipLiveRange {
564564
return .local(allocation, range)
565565
}
566566
}
567+
568+
let addressOwnershipLiveRangeTest = FunctionTest("address_ownership_live_range") {
569+
function, arguments, context in
570+
let address = arguments.takeValue()
571+
print("Address: \(address)")
572+
print("Base: \(address.accessBase)")
573+
let begin = address.definingInstructionOrTerminator ?? {
574+
assert(address is FunctionArgument)
575+
return function.instructions.first!
576+
}()
577+
let localReachabilityCache = LocalVariableReachabilityCache()
578+
guard var ownershipRange = AddressOwnershipLiveRange.compute(for: address, at: begin,
579+
localReachabilityCache, context) else {
580+
print("Error: indeterminate live range")
581+
return
582+
}
583+
defer { ownershipRange.deinitialize() }
584+
print(ownershipRange)
585+
}

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
//
5353
//===----------------------------------------------------------------------===//
5454

55+
import AST
5556
import SIL
5657

5758
private let verbose = false
@@ -593,17 +594,13 @@ struct VariableIntroducerUseDefWalker : LifetimeDependenceUseDefWalker {
593594
}
594595

595596
mutating func walkUp(value: Value, _ owner: Value?) -> WalkResult {
596-
switch value.definingInstruction {
597-
case let moveInst as MoveValueInst:
598-
if moveInst.isFromVarDecl {
599-
return introducer(moveInst, owner)
600-
}
601-
case let borrow as BeginBorrowInst:
602-
if borrow.isFromVarDecl {
603-
return introducer(borrow, owner)
604-
}
605-
default:
606-
break
597+
if let inst = value.definingInstruction, VariableScopeInstruction(inst) != nil {
598+
return introducer(value, owner)
599+
}
600+
// Finding a variable introducer requires following the mark_dependence forwarded value, not the base value like the
601+
// default LifetimeDependenceUseDefWalker.
602+
if value is MarkDependenceInst {
603+
return walkUpDefault(forwarded: value, owner)
607604
}
608605
return walkUpDefault(dependent: value, owner: owner)
609606
}
@@ -749,6 +746,11 @@ extension LifetimeDependenceUseDefWalker {
749746
return walkUp(newLifetime: store.source)
750747
case let srcDestInst as SourceDestAddrInstruction:
751748
return walkUp(address: srcDestInst.sourceOperand.value)
749+
case let apply as FullApplySite:
750+
if let f = apply.referencedFunction,
751+
f.isConvertPointerToPointerArgument {
752+
return walkUp(address: apply.parameterOperands[0].value)
753+
}
752754
default:
753755
break
754756
}

SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ public func registerOptimizerTests() {
154154
// Register each test.
155155
registerFunctionTests(
156156
getAccessBaseTest,
157+
addressOwnershipLiveRangeTest,
157158
argumentConventionsTest,
158159
borrowIntroducersTest,
159160
enclosingValuesTest,

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
4040

4141
public var isAutodiffVJP: Bool { bridged.isAutodiffVJP() }
4242

43+
public var isConvertPointerToPointerArgument: Bool { bridged.isConvertPointerToPointerArgument() }
44+
4345
public var specializationLevel: Int { bridged.specializationLevel() }
4446

4547
public var hasOwnership: Bool { bridged.hasOwnership() }

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,88 @@ public protocol VarDeclInstruction {
401401
var varDecl: VarDecl? { get }
402402
}
403403

404+
/// A scoped instruction whose single result introduces a variable scope.
405+
///
406+
/// The scope-ending uses represent the end of the variable scope. This allows trivial 'let' variables to be treated
407+
/// like a value with ownership. 'var' variables are primarily represented as addressable allocations via alloc_box or
408+
/// alloc_stack, but may have redundant VariableScopeInstructions.
409+
public enum VariableScopeInstruction {
410+
case beginBorrow(BeginBorrowInst)
411+
case moveValue(MoveValueInst)
412+
413+
public init?(_ inst: Instruction?) {
414+
switch inst {
415+
case let bbi as BeginBorrowInst:
416+
guard bbi.isFromVarDecl else {
417+
return nil
418+
}
419+
self = .beginBorrow(bbi)
420+
case let mvi as MoveValueInst:
421+
guard mvi.isFromVarDecl else {
422+
return nil
423+
}
424+
self = .moveValue(mvi)
425+
default:
426+
return nil
427+
}
428+
}
429+
430+
public var instruction: Instruction {
431+
switch self {
432+
case let .beginBorrow(bbi):
433+
return bbi
434+
case let .moveValue(mvi):
435+
return mvi
436+
}
437+
}
438+
439+
public var scopeBegin: Value {
440+
instruction as! SingleValueInstruction
441+
}
442+
443+
public var endOperands: LazyFilterSequence<UseList> {
444+
return scopeBegin.uses.endingLifetime
445+
}
446+
447+
// TODO: with SIL verification, we might be able to make varDecl non-Optional.
448+
public var varDecl: VarDecl? {
449+
if let debugVarDecl = instruction.debugVarDecl {
450+
return debugVarDecl
451+
}
452+
// SILGen may produce double var_decl instructions for the same variable:
453+
// %box = alloc_box [var_decl] "x"
454+
// begin_borrow %box [var_decl]
455+
//
456+
// Assume that, if the begin_borrow or move_value does not have its own debug_value, then it must actually be
457+
// associated with its operand's var_decl.
458+
return instruction.operands[0].value.definingInstruction?.findVarDecl()
459+
}
460+
}
461+
462+
extension Instruction {
463+
/// Find a variable declaration assoicated with this instruction.
464+
public func findVarDecl() -> VarDecl? {
465+
if let varDeclInst = self as? VarDeclInstruction {
466+
return varDeclInst.varDecl
467+
}
468+
if let varScopeInst = VariableScopeInstruction(self) {
469+
return varScopeInst.varDecl
470+
}
471+
return debugVarDecl
472+
}
473+
474+
var debugVarDecl: VarDecl? {
475+
for result in results {
476+
for use in result.uses {
477+
if let debugVal = use.instruction as? DebugValueInst {
478+
return debugVal.varDecl
479+
}
480+
}
481+
}
482+
return nil
483+
}
484+
}
485+
404486
public protocol DebugVariableInstruction : VarDeclInstruction {
405487
typealias DebugVariable = OptionalBridgedSILDebugVariable
406488

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ struct BridgedFunction {
525525
BRIDGED_INLINE void setLinkage(BridgedLinkage linkage) const;
526526
BRIDGED_INLINE void setIsSerialized(bool isSerialized) const;
527527
bool isTrapNoReturn() const;
528+
bool isConvertPointerToPointerArgument() const;
528529
bool isAutodiffVJP() const;
529530
SwiftInt specializationLevel() const;
530531
SWIFT_IMPORT_UNSAFE BridgedSubstitutionMap getMethodSubstitutions(BridgedSubstitutionMap contextSubs) const;

lib/AST/LifetimeDependence.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/DiagnosticsSema.h"
1818
#include "swift/AST/Module.h"
1919
#include "swift/AST/ParameterList.h"
20+
#include "swift/AST/SourceFile.h"
2021
#include "swift/AST/Type.h"
2122
#include "swift/AST/TypeRepr.h"
2223
#include "swift/Basic/Assertions.h"
@@ -538,10 +539,18 @@ LifetimeDependenceInfo::infer(AbstractFunctionDecl *afd) {
538539
: afd->getParameters()->size();
539540

540541
auto *cd = dyn_cast<ConstructorDecl>(afd);
541-
if (cd && cd->isImplicit()) {
542-
if (cd->getParameters()->size() == 0) {
542+
if (cd && cd->getParameters()->size() == 0) {
543+
if (cd->isImplicit()) {
543544
return std::nullopt;
544545
}
546+
if (auto *sf = afd->getParentSourceFile()) {
547+
// The AST printer makes implicit initializers explicit, but does not
548+
// print the @lifetime annotations. Until that is fixed, avoid diagnosing
549+
// this as an error.
550+
if (sf->Kind == SourceFileKind::SIL) {
551+
return std::nullopt;
552+
}
553+
}
545554
}
546555

547556
if (!ctx.LangOpts.hasFeature(Feature::LifetimeDependence)) {

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5069,8 +5069,7 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
50695069
auto lParenLoc = consumeAttributeLParen(); // consume the l_paren
50705070

50715071
std::optional<LifetimeDescriptor> targetDescriptor;
5072-
if (!isInSILMode() &&
5073-
Tok.isAny(tok::identifier, tok::integer_literal, tok::kw_self) &&
5072+
if (Tok.isAny(tok::identifier, tok::integer_literal, tok::kw_self) &&
50745073
peekToken().is(tok::colon)) {
50755074
targetDescriptor = parseLifetimeDescriptor(*this);
50765075
if (!targetDescriptor) {

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,15 @@ bool BridgedFunction::isTrapNoReturn() const {
16081608
return swift::isTrapNoReturnFunction(getFunction());
16091609
}
16101610

1611+
bool BridgedFunction::isConvertPointerToPointerArgument() const {
1612+
if (auto declRef = getFunction()->getDeclRef()) {
1613+
auto *conversionDecl =
1614+
declRef.getASTContext().getConvertPointerToPointerArgument();
1615+
return declRef.getFuncDecl() == conversionDecl;
1616+
}
1617+
return false;
1618+
}
1619+
16111620
bool BridgedFunction::isAutodiffVJP() const {
16121621
return swift::isDifferentiableFuncComponent(
16131622
getFunction(), swift::AutoDiffFunctionComponent::VJP);

0 commit comments

Comments
 (0)