mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Improves OSSALifetimeCompletion to handle trivial variables when running from
SILGenCleanup. This only affects lifetime dependence diagnostics.
For the purpose of lifetime diagnostics, trivial local variables are only valid
within their lexical scope. Sadly, SILGen only know how to insert cleanup code
on normal function exits. SILGenCleanup relies on lifetime completion to fix
lifetimes on dead end paths.
%var = move_value [var_decl]
try_apply %f() : $..., normal bb1, error error
error:
extend_lifetime %var <=== insert this
unreachable
This allows Span to depend on local unsafe pointers AND be used within
throwing closures:
_ = a.withUnsafeBufferPointer {
let buffer = $0
let view = Span(_unsafeElements: buffer)
return view.withUnsafeBufferPointer(\.count)
}
214 lines
7.5 KiB
C++
214 lines
7.5 KiB
C++
//===--- OwnershipLifetimeCompletion.h ------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// OSSA lifetime completion adds lifetime ending instructions to make
|
|
/// linear lifetimes complete.
|
|
///
|
|
/// Completion is bottom-up recursive over nested borrow scopes. Additionally,
|
|
/// this may be extended to support dependent owned lifetimes in the future to
|
|
/// handle owned non-escaping values.
|
|
///
|
|
/// Lexical lifetimes can only be incomplete as a result of dead-end blocks. In
|
|
/// this case, their lifetime ends immediately before the dead-end block.
|
|
///
|
|
/// Nonlexical lifetimes can be incomplete for any reason. Their lifetime ends
|
|
/// at the liveness boundary.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SILOPTIMIZER_UTILS_OSSSALIFETIMECOMPLETION_H
|
|
#define SWIFT_SILOPTIMIZER_UTILS_OSSSALIFETIMECOMPLETION_H
|
|
|
|
#include "swift/SIL/NodeDatastructures.h"
|
|
#include "swift/SIL/OwnershipLiveness.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
|
|
namespace swift {
|
|
|
|
class DeadEndBlocks;
|
|
|
|
enum class LifetimeCompletion { NoLifetime, AlreadyComplete, WasCompleted };
|
|
|
|
class OSSALifetimeCompletion {
|
|
public:
|
|
enum HandleTrivialVariable_t { IgnoreTrivialVariable, ExtendTrivialVariable };
|
|
|
|
private:
|
|
// If domInfo is nullptr, then InteriorLiveness never assumes dominance. As a
|
|
// result it may report extra unenclosedPhis. In that case, any attempt to
|
|
// create a new phi would result in an immediately redundant phi.
|
|
const DominanceInfo *domInfo = nullptr;
|
|
|
|
DeadEndBlocks &deadEndBlocks;
|
|
|
|
// Cache intructions already handled by the recursive algorithm to avoid
|
|
// recomputing their lifetimes.
|
|
ValueSet completedValues;
|
|
|
|
// Extend trivial variables for lifetime diagnostics (only in SILGenCleanup).
|
|
HandleTrivialVariable_t handleTrivialVariable;
|
|
|
|
public:
|
|
OSSALifetimeCompletion(SILFunction *function, const DominanceInfo *domInfo,
|
|
DeadEndBlocks &deadEndBlocks,
|
|
HandleTrivialVariable_t handleTrivialVariable = IgnoreTrivialVariable)
|
|
: domInfo(domInfo), deadEndBlocks(deadEndBlocks),
|
|
completedValues(function), handleTrivialVariable(handleTrivialVariable) {}
|
|
|
|
// The kind of boundary at which to complete the lifetime.
|
|
//
|
|
// Liveness: "As early as possible." Consume the value after the last
|
|
// non-consuming uses.
|
|
// Availability: "As late as possible." Consume the value in the last blocks
|
|
// beyond the non-consuming uses in which the value has been
|
|
// consumed on no incoming paths.
|
|
struct Boundary {
|
|
enum Value : uint8_t {
|
|
Liveness,
|
|
Availability,
|
|
};
|
|
Value value;
|
|
|
|
Boundary(Value value) : value(value){};
|
|
operator Value() const { return value; }
|
|
};
|
|
|
|
/// Insert a lifetime-ending instruction on every path to complete the OSSA
|
|
/// lifetime of \p value along \p boundary.
|
|
///
|
|
/// If \p boundary is not specified, the following boundary will be used:
|
|
/// \p value is lexical -> Boundary::Availability
|
|
/// \p value is non-lexical -> Boundary::Liveness
|
|
///
|
|
/// Lifetime completion is only relevant for owned
|
|
/// values or borrow introducers.
|
|
///
|
|
/// Currently \p boundary == {Boundary::Availability} is used by Mem2Reg and
|
|
/// TempRValueOpt and PredicatbleMemOpt to complete lexical enum values on
|
|
/// trivial paths.
|
|
///
|
|
/// Returns true if any new instructions were created to complete the
|
|
/// lifetime.
|
|
LifetimeCompletion completeOSSALifetime(SILValue value, Boundary boundary) {
|
|
switch (value->getOwnershipKind()) {
|
|
case OwnershipKind::None: {
|
|
if (auto scopedAddress = ScopedAddressValue(value)) {
|
|
break;
|
|
}
|
|
// During SILGenCleanup, extend move_value [var_decl].
|
|
if (handleTrivialVariable == ExtendTrivialVariable
|
|
&& value->isFromVarDecl()) {
|
|
break;
|
|
}
|
|
return LifetimeCompletion::NoLifetime;
|
|
}
|
|
case OwnershipKind::Owned:
|
|
break;
|
|
case OwnershipKind::Any:
|
|
llvm::report_fatal_error("value with any ownership kind!?");
|
|
case OwnershipKind::Guaranteed:
|
|
case OwnershipKind::Unowned: {
|
|
BorrowedValue borrowedValue(value);
|
|
if (!borrowedValue)
|
|
return LifetimeCompletion::NoLifetime;
|
|
|
|
if (!borrowedValue.isLocalScope())
|
|
return LifetimeCompletion::AlreadyComplete;
|
|
}
|
|
}
|
|
|
|
if (!completedValues.insert(value))
|
|
return LifetimeCompletion::AlreadyComplete;
|
|
|
|
return analyzeAndUpdateLifetime(value, boundary)
|
|
? LifetimeCompletion::WasCompleted
|
|
: LifetimeCompletion::AlreadyComplete;
|
|
}
|
|
|
|
enum class LifetimeEnd : uint8_t {
|
|
/// The lifetime ends at the boundary.
|
|
Boundary,
|
|
/// The lifetime "ends" in a loop.
|
|
Loop,
|
|
};
|
|
|
|
static void visitAvailabilityBoundary(
|
|
SILValue value, const SSAPrunedLiveness &liveness,
|
|
llvm::function_ref<void(SILInstruction *, LifetimeEnd end)> visit);
|
|
|
|
protected:
|
|
bool analyzeAndUpdateLifetime(SILValue value, Boundary boundary);
|
|
bool analyzeAndUpdateLifetime(ScopedAddressValue value, Boundary boundary);
|
|
};
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// UnreachableLifetimeCompletion
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Fixup OSSA before deleting an unreachable code path.
|
|
///
|
|
/// Only needed when a code path reaches a no-return function, making the
|
|
/// path now partially unreachable. Conditional branch folding requires no fixup
|
|
/// because it causes the entire path to become unreachable.
|
|
class UnreachableLifetimeCompletion {
|
|
SILFunction *function;
|
|
|
|
// If domInfo is nullptr, lifetime completion may attempt to recreate
|
|
// redundant phis, which should be immediately discarded.
|
|
const DominanceInfo *domInfo = nullptr;
|
|
DeadEndBlocks &deadEndBlocks;
|
|
|
|
BasicBlockSetVector unreachableBlocks;
|
|
InstructionSet unreachableInsts; // not including those in unreachableBlocks
|
|
ValueSetVector incompleteValues;
|
|
bool updatingLifetimes = false;
|
|
|
|
public:
|
|
UnreachableLifetimeCompletion(SILFunction *function, DominanceInfo *domInfo,
|
|
DeadEndBlocks &deadEndBlocks)
|
|
: function(function), domInfo(domInfo), deadEndBlocks(deadEndBlocks),
|
|
unreachableBlocks(function), unreachableInsts(function),
|
|
incompleteValues(function) {}
|
|
|
|
/// Record information about this unreachable instruction and return true if
|
|
/// ends any simple OSSA lifetimes.
|
|
///
|
|
/// Note: this must be called in forward order so that lifetime completion
|
|
/// runs from the inside out.
|
|
void visitUnreachableInst(SILInstruction *instruction);
|
|
|
|
void visitUnreachableBlock(SILBasicBlock *block) {
|
|
unreachableBlocks.insert(block);
|
|
}
|
|
|
|
/// Complete the lifetime of any value defined outside of the unreachable
|
|
/// region that was previously destroyed in the unreachable region.
|
|
bool completeLifetimes();
|
|
};
|
|
|
|
inline llvm::raw_ostream &
|
|
operator<<(llvm::raw_ostream &OS, OSSALifetimeCompletion::Boundary boundary) {
|
|
switch (boundary) {
|
|
case OSSALifetimeCompletion::Boundary::Liveness:
|
|
OS << "liveness";
|
|
break;
|
|
case OSSALifetimeCompletion::Boundary::Availability:
|
|
OS << "availability";
|
|
break;
|
|
}
|
|
return OS;
|
|
}
|
|
|
|
} // namespace swift
|
|
|
|
#endif
|