mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Rig TransitiveAddressWalker to keep track of enough information for passes to correctly check for pointer escapes and dependence uses. Requires for precise bail-outs and asserts.
311 lines
11 KiB
C++
311 lines
11 KiB
C++
//===--- OwnershipLiveness.h ---------------------------------*- C++ -*----===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// Terminology:
|
|
///
|
|
/// Linear lifetime: All paths through the value's definition to the
|
|
/// function exit pass through exactly one lifetime-ending operation.
|
|
///
|
|
/// Complete OSSA: All owned values and borrow introducers have linear lifetime.
|
|
///
|
|
/// Borrow introducer: defines a guaranteed SILValue and an associated explicit
|
|
/// borrow scope: begin_borrow, load_borrow, store_borrow, and reborrow. A
|
|
/// "reborrow" is a guaranteed phi for which `isGuaranteedForwarding` returns
|
|
/// false. (Eventually, the phi will know whether it is a reborrow based on a
|
|
/// flag or subclass). Guaranteed function arguments do conceptually introduce
|
|
/// borrow scopes, but their scopes are implied by the function entry and
|
|
/// return points.
|
|
///
|
|
/// Completing an OSSA lifetime is the process of giving an owned value or a
|
|
/// borrow introducer a linear lifetime. This process may require phi creation
|
|
/// whenever a nested scope involves a guaranteed phi that is not dominated by
|
|
/// the outer scope's definition. A new phi will be created that ends the outer
|
|
/// lifetime and begins a new lifetime in the successor block. This new phi will
|
|
/// either forward an owned value or reborrow the outer borrow scope. The new
|
|
/// phi's lifetime will then be recursively extended over the lifetime of the
|
|
/// original guaranteed phi, which we refer to as its inner adjacent phi.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// Linear SSA liveness:
|
|
///
|
|
/// - Liveness for a single "defining" owned value or borrow introducing value
|
|
/// assuming the value's lifetime is already linear.
|
|
///
|
|
/// - The definition dominates all use points (phis do not extend the lifetime)
|
|
///
|
|
/// - Only lifetime-ending operations generate liveness
|
|
///
|
|
/// - Oblivious to pointer escapes within the lifetime
|
|
///
|
|
///
|
|
/// Interior SSA liveness:
|
|
///
|
|
/// - Liveness for a single "defining" value with any ownership.
|
|
///
|
|
/// - The definition dominates all use points (phis do not extend the lifetime)
|
|
///
|
|
/// - Does not assume the current lifetime is linear. Transitively follows
|
|
/// guaranteed forwarding and address uses within the current scope.
|
|
///
|
|
/// - Liveness cannot extend beyond lifetime-ending operations
|
|
/// (a.k.a. affine lifetimes).
|
|
///
|
|
/// - Assumes inner scopes *are* linear, including borrow and address scopes
|
|
/// (e.g. begin_borrow, load_borrow, begin_apply, store_borrow, begin_access)
|
|
/// A callback may be used to complete inner scopes before updating liveness.
|
|
///
|
|
/// - Only returns AddressUseKind::PointerEscape if one of the uses of the
|
|
/// outer value has OperandOwnership::PointerEscape or
|
|
/// OperandOwnership::BitwiseEscape. An inner scope's guaranteed value may
|
|
/// escape without causing the outer scope's value to escape.
|
|
///
|
|
/// - Insulates outer scopes from inner scope details. Maintains the
|
|
/// invariant that inlining cannot pessimize optimization.
|
|
///
|
|
/// - Interior SSA liveness is used to complete (linearize) an OSSA lifetime
|
|
///
|
|
/// Interior liveness example:
|
|
///
|
|
/// %struct = struct ...
|
|
/// %f = struct_extract %s // defines a guaranteed value (%f)
|
|
/// %b = begin_borrow %field
|
|
/// %a = ref_element_addr %b
|
|
/// _ = address_to_pointer %a
|
|
/// end_borrow %b // the only interior use of %f
|
|
///
|
|
/// When computing interior liveness for %f, %b is an inner scope. Because inner
|
|
/// scopes are complete, the only relevant use is end_borrow %b. Despite the
|
|
/// address_to_pointer instruction, %f does not escape any dependent address.
|
|
///
|
|
/// Transitive SSA liveness
|
|
///
|
|
/// - Similar to Interior SSA liveness, but does not assume that any lifetimes
|
|
/// are linear. Transitively follows uses within inner scopes, recursively
|
|
/// through nested scopes, including forwarding operations and address uses.
|
|
///
|
|
/// - Much more likely to return AddressUseKind::PointerEscape
|
|
///
|
|
/// Transitive liveness example:
|
|
///
|
|
/// %struct = struct ...
|
|
/// %f = struct_extract %s // defines a guaranteed value (%f)
|
|
/// %b = begin_borrow %field
|
|
/// %a = ref_element_addr %b
|
|
/// _ = address_to_pointer %a // a transitive use of %f escapes
|
|
///
|
|
/// When computing transitive liveness for %f, %b is an inner scope. Liveness
|
|
/// does not assume that an end_borrow exists. Instead it transitively considers
|
|
/// all uses of %b. As a result, %f escapes.
|
|
///
|
|
/// - This header provides no interface for transitive liveness because it is no
|
|
/// longer needed with complete OSSA lifetimes
|
|
///
|
|
/// Extended liveness
|
|
///
|
|
/// - Liveness of a single "defining" value extended beyond its lifetime-ending
|
|
/// operations.
|
|
///
|
|
/// - May refer to copy-extension, phi-extension, or extension over barriers
|
|
/// such as access markers.
|
|
///
|
|
/// - For phi-extension, the definition no longer dominates the use
|
|
/// points. MultiDefPrunedLiveness must be used. Each phi is added as a new
|
|
/// definition.
|
|
///
|
|
/// Extended linear liveness
|
|
///
|
|
/// - Extended liveness that only considers lifetime-ending operations. Assumes
|
|
/// the definition already has a linear lifetime and that any phis that end
|
|
/// the current lifetime also have linear lifetimes.
|
|
///
|
|
/// Extended interior liveness
|
|
///
|
|
/// - Like interior SSA liveness, does not assume the current lifetime is
|
|
/// linear. Transitively follows guaranteed forwarding and address uses within
|
|
/// the current scope. Assumes inner scopes *are* linear.
|
|
///
|
|
/// - Interior copy-extension is used to canonicalize an OSSA lifetime
|
|
///
|
|
/// - This header provides no interface for extended interior liveness because
|
|
/// it is highly specific to a particular task.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_OWNERSHIPLIVENESS_H
|
|
#define SWIFT_SIL_OWNERSHIPLIVENESS_H
|
|
|
|
#include "swift/Basic/Debug.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/SIL/PrunedLiveness.h"
|
|
#include "swift/SIL/OwnershipUseVisitor.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBasicBlock.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILValue.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
namespace swift {
|
|
|
|
/// Analyze SSA liveness of values that introduce an OSSA live range:
|
|
///
|
|
/// 1. Owned non-phi values
|
|
/// 2. Owned phi values
|
|
/// 3. Borrow scope introducers: begin_borrow/load_borrow
|
|
/// 4. Reborrows: guaranteed phis that end their operands' borrow scopes and
|
|
/// require their own post-dominating end_borrows.
|
|
///
|
|
/// Used for OSSA lifetime completion.
|
|
class OSSALiveness {
|
|
protected:
|
|
SILValue ownershipDef;
|
|
|
|
// MARK: Results.
|
|
|
|
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
|
|
SSAPrunedLiveness liveness;
|
|
|
|
OSSALiveness(const OSSALiveness &) = delete;
|
|
OSSALiveness &operator=(const OSSALiveness &) = delete;
|
|
|
|
public:
|
|
OSSALiveness(SILValue def): ownershipDef(def),
|
|
liveness(def->getFunction(), &discoveredBlocks) {}
|
|
|
|
const SSAPrunedLiveness &getLiveness() const { return liveness; }
|
|
|
|
ArrayRef<SILBasicBlock *> getDiscoveredBlocks() const {
|
|
return discoveredBlocks;
|
|
}
|
|
|
|
void print(llvm::raw_ostream &OS) const;
|
|
void dump() const;
|
|
};
|
|
|
|
// Internal implementation
|
|
struct LinearLivenessVisitor;
|
|
|
|
/// Compute ownershipDef's lifetime based on it's lifetime-ending uses, assuming
|
|
/// it is already complete/linear. ownershipDef must be either an owned value or
|
|
/// a local borrow scope introduced (begin_borrow, load_borrow, or
|
|
/// store_borrow).
|
|
///
|
|
/// This is the simplest OSSA liveness analysis, but is not appropriate for
|
|
/// fixing OSSA lifetimes after a transformation and cannot tell you whether
|
|
/// pointer escapes occur.
|
|
class LinearLiveness : public OSSALiveness {
|
|
friend LinearLivenessVisitor;
|
|
|
|
public:
|
|
/// Whether extend_lifetime instructions should be added to the boundary.
|
|
/// Used to verify extend_lifetime instructions.
|
|
enum IncludeExtensions_t {
|
|
DoNotIncludeExtensions = false,
|
|
IncludeExtensions = true,
|
|
};
|
|
LinearLiveness(SILValue def,
|
|
IncludeExtensions_t includeExtensions = IncludeExtensions);
|
|
|
|
void compute();
|
|
|
|
private:
|
|
const IncludeExtensions_t includeExtensions;
|
|
};
|
|
|
|
// Internal implementation
|
|
struct InteriorLivenessVisitor;
|
|
|
|
/// Compute ownershipDef's lifetime based on all its uses (except those
|
|
/// already enclosed by inner scopes). Returns AddressUseKind::PointerEscape
|
|
/// if a pointer to ownershipDef escapes (and is not already enclosed by an
|
|
/// inner scope).
|
|
class InteriorLiveness : public OSSALiveness {
|
|
friend InteriorLivenessVisitor;
|
|
|
|
// Handle inner scopes. Called for inner reborrows, inner adjacent reborrows,
|
|
// and address scopes.
|
|
//
|
|
// This may add uses to the inner scope, but it may not modify a use-list
|
|
// in any outer scopes.
|
|
using InnerScopeHandlerRef = llvm::function_ref<void(SILValue)>;
|
|
|
|
public:
|
|
// Summarize address uses
|
|
AddressUseKind addressUseKind = AddressUseKind::Unknown;
|
|
Operand *escapingUse = nullptr;
|
|
|
|
public:
|
|
InteriorLiveness(SILValue def): OSSALiveness(def) {}
|
|
|
|
void compute(const DominanceInfo *domInfo,
|
|
InnerScopeHandlerRef handleInnerScope = InnerScopeHandlerRef());
|
|
|
|
/// Compute the boundary from the blocks discovered during liveness analysis.
|
|
void computeBoundary(PrunedLivenessBoundary &boundary) const {
|
|
liveness.computeBoundary(boundary);
|
|
}
|
|
|
|
AddressUseKind getAddressUseKind() const { return addressUseKind; }
|
|
|
|
void print(llvm::raw_ostream &OS) const;
|
|
void dump() const;
|
|
};
|
|
|
|
// Internal implementation
|
|
struct ExtendedLinearLivenessVisitor;
|
|
|
|
/// Analyze liveness of values that introduce an OSSA live range. This computes
|
|
/// the phi-extended live range for these four categories of live range
|
|
/// introducing values:
|
|
///
|
|
/// 1. Owned non-phi values
|
|
/// 2. Owned phi values
|
|
/// 3. Borrow scope introducers: begin_borrow/load_borrow
|
|
/// 4. Reborrows: guaranteed phis that end their incoming borrow scopes and
|
|
/// begin a new borrow scope
|
|
class ExtendedLinearLiveness {
|
|
friend ExtendedLinearLivenessVisitor;
|
|
|
|
SILValue ownershipDef;
|
|
|
|
// MARK: Results.
|
|
|
|
// Because of reborrows, the ssa def may not dominate all
|
|
// uses. Consider the reborrows to be separate defs.
|
|
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
|
|
MultiDefPrunedLiveness liveness;
|
|
|
|
ExtendedLinearLiveness(const ExtendedLinearLiveness &) = delete;
|
|
ExtendedLinearLiveness &operator=(const ExtendedLinearLiveness &) = delete;
|
|
|
|
public:
|
|
ExtendedLinearLiveness(SILValue def);
|
|
|
|
void compute();
|
|
|
|
/// The array of defs. The first element is ownershipDef. The remaining
|
|
/// elements are outer reborrows discovered during computation.
|
|
///
|
|
/// TODO: These are always SILValues. Convert the iterator.
|
|
NodeSetVector::iterator defBegin() const { return liveness.defBegin(); }
|
|
NodeSetVector::iterator defEnd() const { return liveness.defBegin(); }
|
|
|
|
const MultiDefPrunedLiveness &getLiveness() const { return liveness; }
|
|
|
|
void print(llvm::raw_ostream &OS) const;
|
|
void dump() const;
|
|
};
|
|
|
|
} // namespace swift
|
|
|
|
#endif
|