Skip to content

[nfc] LifetimeDiagnostics cleanup #75562

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 7 commits into from
Jul 30, 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
8 changes: 8 additions & 0 deletions SwiftCompilerSources/Sources/Basic/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ public func precondition(_ condition: Bool, _ message: @autoclosure () -> String
// Debugging Utilities
//===----------------------------------------------------------------------===//

public func debugLog(prefix: Bool = true, _ message: @autoclosure () -> String) {
let formatted = (prefix ? "### " : "") + message()
formatted._withBridgedStringRef { ref in
Bridged_dbgs().write(ref)
}
Bridged_dbgs().newLine()
}

/// Let's lldb's `po` command not print any "internal" properties of the conforming type.
///
/// This is useful if the `description` already contains all the information of a type instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ private let verbose = false

private func log(prefix: Bool = true, _ message: @autoclosure () -> String) {
if verbose {
print((prefix ? "### " : "") + message())
debugLog(prefix: prefix, message())
}
}

Expand All @@ -36,6 +36,7 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
#endif
log(prefix: false, "\n--- Diagnosing lifetime dependence in \(function.name)")
log("\(function)")
log("\(function.convention)")

for argument in function.arguments
where !argument.type.isEscapable(in: function)
Expand All @@ -54,8 +55,8 @@ let lifetimeDependenceDiagnosticsPass = FunctionPass(
continue
}
if let apply = instruction as? FullApplySite {
// Handle ~Escapable results that do not have a lifetime
// dependence (@_unsafeNonescapableResult).
// Handle ~Escapable results that do not have a lifetime dependence. This includes implicit initializers and
// @_unsafeNonescapableResult.
apply.resultOrYields.forEach {
if let lifetimeDep = LifetimeDependence(unsafeApplyResult: $0,
context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ private func insertDependencies(for apply: LifetimeDependentApply,
insertMarkDependencies(value: dependentValue, initializer: nil,
bases: bases, builder: builder, context)
}
let builder = Builder(after: apply.applySite, context)
for resultOper in apply.applySite.indirectResultOperands {
let accessBase = resultOper.value.accessBase
guard let (initialAddress, initializingStore) =
Expand All @@ -132,11 +131,11 @@ private func insertDependencies(for apply: LifetimeDependentApply,
context) else {
continue
}
assert(initializingStore == resultOper.instruction,
"an indirect result is a store")
insertMarkDependencies(value: initialAddress,
initializer: initializingStore, bases: bases,
builder: builder, context)
assert(initializingStore == resultOper.instruction, "an indirect result is a store")
Builder.insert(after: apply.applySite, context) { builder in
insertMarkDependencies(value: initialAddress, initializer: initializingStore, bases: bases, builder: builder,
context)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ private func optimizeNonOptionalBridging(_ apply: ApplyInst,
private func removeBridgingCodeInPredecessors(of block: BasicBlock, _ context: FunctionPassContext) {
for pred in block.predecessors {
let branch = pred.terminator as! BranchInst
let builder = Builder(after: branch, context)
let builder = Builder(atEndOf: branch.parentBlock, location: branch.location, context)
builder.createBranch(to: block)

let en = branch.operands[0].value as! EnumInst
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,19 +492,23 @@ extension Builder {
}

/// Creates a builder which inserts _after_ `insPnt`, using a custom `location`.
///
/// TODO: this is usually incorrect for terminator instructions. Instead use
/// `Builder.insert(after:location:_:insertFunc)` from OptUtils.swift. Rename this to afterNonTerminator.
init(after insPnt: Instruction, location: Location, _ context: some MutatingContext) {
context.verifyIsTransforming(function: insPnt.parentFunction)
if let nextInst = insPnt.next {
self.init(insertAt: .before(nextInst), location: location,
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
} else {
self.init(insertAt: .atEndOf(insPnt.parentBlock), location: location,
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
guard let nextInst = insPnt.next else {
fatalError("cannot insert an instruction after a block terminator.")
}
self.init(insertAt: .before(nextInst), location: location,
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
}

/// Creates a builder which inserts _after_ `insPnt`, using `insPnt`'s next
/// non-meta instruction's location.
///
/// TODO: this is incorrect for terminator instructions. Instead use `Builder.insert(after:location:_:insertFunc)`
/// from OptUtils.swift. Rename this to afterNonTerminator.
init(after insPnt: Instruction, _ context: some MutatingContext) {
self.init(after: insPnt, location: insPnt.locationOfNextNonMetaInstruction, context)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ private func log(_ message: @autoclosure () -> String) {
///
/// TODO: Integrate this with SIL verification to ensure completeness.
///
/// TODO: Convert AddressDefUseWalker to conform to AddressUtils after
/// TODO: Convert AddressDefUseWalker to use AddressUseVisitor after
/// checking that the additional instructions are handled correctly by
/// escape analysis.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ private func createForwardingApply(
isNonThrowing: ai.isNonThrowing,
isNonAsync: ai.isNonAsync,
specializationInfo: ai.specializationInfo)
let builder = Builder(after: newApply, context)
builder.createReturn(of: newApply)
case let tai as TryApplyInst:
let normalBlock = thunk.appendNewBlock(context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ struct LifetimeDependence : CustomStringConvertible {
case caller(Argument)
/// An access scope.
case access(BeginAccessInst)
/// An coroutine.
/// A coroutine.
case yield(Value)
/// An owned value whose OSSA lifetime encloses nonescapable values
case owned(Value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ extension FullApplySite {
}

extension Builder {
static func insert(after inst: Instruction, _ context: some MutatingContext, insertFunc: (Builder) -> ()) {
Builder.insert(after: inst, location: inst.location, context, insertFunc: insertFunc)
}

static func insert(after inst: Instruction, location: Location,
_ context: some MutatingContext, insertFunc: (Builder) -> ()) {
if inst is TermInst {
Expand Down
16 changes: 16 additions & 0 deletions SwiftCompilerSources/Sources/SIL/ApplySite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ public struct ApplyOperandConventions : Collection {
calleeArgumentIndex(ofOperandIndex: operandIndex)!]
}

public subscript(parameterDependencies operandIndex: Int)
-> FunctionConvention.LifetimeDependencies? {
return calleeArgumentConventions[parameterDependencies:
calleeArgumentIndex(ofOperandIndex: operandIndex)!]
}

public var firstParameterOperandIndex: Int {
return ApplyOperandConventions.firstArgumentIndex +
calleeArgumentConventions.firstParameterIndex
Expand Down Expand Up @@ -222,6 +228,16 @@ extension ApplySite {
functionConvention.resultDependencies != nil
}

public var hasLifetimeDependence: Bool {
functionConvention.hasLifetimeDependencies()
}

public func parameterDependencies(target operand: Operand) -> FunctionConvention.LifetimeDependencies? {
let idx = operand.index
return idx < operandConventions.startIndex ? nil
: operandConventions[parameterDependencies: idx]
}

public var yieldConventions: YieldConventions {
YieldConventions(convention: functionConvention)
}
Expand Down
32 changes: 26 additions & 6 deletions SwiftCompilerSources/Sources/SIL/Argument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,22 @@ public struct ArgumentConventions : Collection, CustomStringConvertible {
return convention.parameters[paramIdx]
}

/// Return a dependence of the function results on the indexed parameter.
public subscript(resultDependsOn argumentIndex: Int)
-> LifetimeDependenceConvention? {
guard let paramIdx = parameterIndex(for: argumentIndex) else {
public subscript(parameterDependencies targetArgumentIndex: Int) -> FunctionConvention.LifetimeDependencies? {
guard let targetParamIdx = parameterIndex(for: targetArgumentIndex) else {
return nil
}
return convention.resultDependencies?[paramIdx]
return convention.parameterDependencies(for: targetParamIdx)
}

/// Return a dependence of the function results on the indexed parameter.
public subscript(resultDependsOn argumentIndex: Int) -> LifetimeDependenceConvention? {
findDependence(source: argumentIndex, in: convention.resultDependencies)
}

/// Return a dependence of the target argument on the source argument.
public func getDependence(target targetArgumentIndex: Int, source sourceArgumentIndex: Int)
-> LifetimeDependenceConvention? {
findDependence(source: sourceArgumentIndex, in: self[parameterDependencies: targetArgumentIndex])
}

/// Number of SIL arguments for the function type's results
Expand Down Expand Up @@ -282,8 +291,11 @@ public struct ArgumentConventions : Collection, CustomStringConvertible {
}
for idx in indirectSILResultCount..<endIndex {
str += "\n[\(idx)] parameter: " + self[idx].description
if let deps = self[parameterDependencies: idx] {
str += "\n lifetime: \(deps)"
}
if let dep = self[resultDependsOn: idx] {
str += "resultDependsOn: " + dep.description
str += "\n result dependence: " + dep.description
}
}
return str
Expand All @@ -295,6 +307,14 @@ extension ArgumentConventions {
let firstParamIdx = firstParameterIndex // bridging call
return argIdx < firstParamIdx ? nil : argIdx - firstParamIdx
}

private func findDependence(source argumentIndex: Int, in dependencies: FunctionConvention.LifetimeDependencies?)
-> LifetimeDependenceConvention? {
guard let paramIdx = parameterIndex(for: argumentIndex) else {
return nil
}
return dependencies?[paramIdx]
}
}

public struct YieldConventions : Collection, CustomStringConvertible {
Expand Down
70 changes: 33 additions & 37 deletions SwiftCompilerSources/Sources/SIL/FunctionConvention.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,31 +73,33 @@ public struct FunctionConvention : CustomStringConvertible {
hasLoweredAddresses: hasLoweredAddresses)
}

/// If the function result depends on any parameters, return a
/// Collection of LifetimeDependenceConvention indexed on the
/// function parameter.
public var resultDependencies: ResultDependencies? {
let bridgedDependencies = bridgedFunctionType.SILFunctionType_getLifetimeDependencies()
let dependencies = LifetimeDependencies(bridged: bridgedDependencies)
let targetIndex = parameters.count

for dependence in dependencies {
if dependence.getTargetIndex() == targetIndex {
return ResultDependencies(bridged: dependence,
parameterCount: parameters.count,
hasSelfParameter: hasSelfParameter)
}
}
return nil
/// If the function result depends on any parameters, return a Collection of LifetimeDependenceConventions for the
/// dependence source parameters.
public var resultDependencies: LifetimeDependencies? {
lifetimeDependencies(for: parameters.count)
}

/// If the parameter indexed by 'targetParameterIndex' is the target of any dependencies on other parameters, return a
/// Collection of LifetimeDependenceConventions for the dependence source parameters.
public func parameterDependencies(for targetParameterIndex: Int) -> LifetimeDependencies? {
lifetimeDependencies(for: targetParameterIndex)
}

public func hasLifetimeDependencies() -> Bool {
return bridgedFunctionType.SILFunctionType_getLifetimeDependencies().count() != 0
}

public var description: String {
var str = String(taking: bridgedFunctionType.getDebugDescription())
parameters.forEach { str += "\nparameter: " + $0.description }
for paramIdx in 0..<parameters.count {
str += "\nparameter: " + parameters[paramIdx].description
if let deps = parameterDependencies(for: paramIdx) {
str += "\n lifetime: \(deps)"
}
}
results.forEach { str += "\n result: " + $0.description }
str += (hasLoweredAddresses ? "\n[lowered_address]" : "\n[sil_opaque]")
if let deps = resultDependencies {
str += "\nresult dependences \(deps)"
str += "\n lifetime: \(deps)"
}
return str
}
Expand Down Expand Up @@ -248,28 +250,22 @@ public enum LifetimeDependenceConvention : CustomStringConvertible {
}

extension FunctionConvention {
struct LifetimeDependencies : Collection {
let bridged: BridgedLifetimeDependenceInfoArray

var startIndex: Int { 0 }

var endIndex: Int { bridged.count() }

func index(after index: Int) -> Int {
return index + 1
}
// Create a Swift LifetimeDependenceInfo for BridgedLifetimeDependenceInfo if this method needs
// to be exposed outside FunctionConvention.
// That will likely need bridging IndexSubset to Swift.
subscript(_ index: Int) -> BridgedLifetimeDependenceInfo {
return bridged.at(index)
// 'targetIndex' is either the parameter index or parameters.count for the function result.
private func lifetimeDependencies(for targetIndex: Int) -> LifetimeDependencies? {
let bridgedDependenceInfoArray = bridgedFunctionType.SILFunctionType_getLifetimeDependencies()
for infoIndex in 0..<bridgedDependenceInfoArray.count() {
let bridgedDependenceInfo = bridgedDependenceInfoArray.at(infoIndex)
if bridgedDependenceInfo.targetIndex == targetIndex {
return LifetimeDependencies(bridged: bridgedDependenceInfo,
parameterCount: parameters.count,
hasSelfParameter: hasSelfParameter)
}
}
return nil
}
}

extension FunctionConvention {
/// Collection of LifetimeDependenceConvention? that parallels parameters.
public struct ResultDependencies : Collection, CustomStringConvertible {
public struct LifetimeDependencies : Collection, CustomStringConvertible {
let bridged: BridgedLifetimeDependenceInfo
let paramCount: Int
let hasSelfParam: Bool
Expand Down
41 changes: 35 additions & 6 deletions include/swift/Basic/BasicBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,12 @@ enum ENUM_EXTENSIBILITY_ATTR(open) BridgedFeature {
#include "swift/Basic/Features.def"
};

//===----------------------------------------------------------------------===//
// MARK: OStream
//===----------------------------------------------------------------------===//

BRIDGING_WRAPPER_NONNULL(llvm::raw_ostream, OStream)

//===----------------------------------------------------------------------===//
// MARK: StringRef
//===----------------------------------------------------------------------===//

class BridgedOStream;

class BridgedStringRef {
const char *_Nullable Data;
size_t Length;
Expand Down Expand Up @@ -250,6 +246,39 @@ BRIDGED_INLINE SwiftInt BridgedOwnedString_count(BridgedOwnedString str);
SWIFT_NAME("getter:BridgedOwnedString.isEmpty(self:)")
BRIDGED_INLINE bool BridgedOwnedString_empty(BridgedOwnedString str);

//===----------------------------------------------------------------------===//
// MARK: OStream
//===----------------------------------------------------------------------===//

class BridgedOStream {
llvm::raw_ostream * _Nonnull os;

public:
SWIFT_UNAVAILABLE("Use init(raw:) instead")
BridgedOStream(llvm::raw_ostream * _Nonnull os) : os(os) {}

SWIFT_UNAVAILABLE("Use '.raw' instead")
llvm::raw_ostream * _Nonnull unbridged() const { return os; }

void write(BridgedStringRef string) const;

void newLine() const;

void flush() const;
};

SWIFT_NAME("getter:BridgedOStream.raw(self:)")
inline void * _Nonnull BridgedOStream_getRaw(BridgedOStream bridged) {
return bridged.unbridged();
}

SWIFT_NAME("BridgedOStream.init(raw:)")
inline BridgedOStream BridgedOStream_fromRaw(void * _Nonnull os) {
return static_cast<llvm::raw_ostream *>(os);
}

BridgedOStream Bridged_dbgs();

//===----------------------------------------------------------------------===//
// MARK: SourceLoc
//===----------------------------------------------------------------------===//
Expand Down
Loading