mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1618 lines
60 KiB
C++
1618 lines
60 KiB
C++
//===--- DIMemoryUseCollector.cpp - Memory use analysis for DI ------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "definite-init"
|
|
#include "DIMemoryUseCollector.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
#ifdef SWIFT_SILOPTIMIZER_PASSMANAGER_DIMEMORYUSECOLLECTOROWNERSHIP_H
|
|
#error "Included ownership header?!"
|
|
#endif
|
|
|
|
using namespace swift;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DIMemoryObjectInfo Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static unsigned getElementCountRec(SILModule &Module,
|
|
SILType T,
|
|
bool IsSelfOfNonDelegatingInitializer) {
|
|
// If this is a tuple, it is always recursively flattened.
|
|
if (CanTupleType TT = T.getAs<TupleType>()) {
|
|
assert(!IsSelfOfNonDelegatingInitializer && "self never has tuple type");
|
|
unsigned NumElements = 0;
|
|
for (unsigned i = 0, e = TT->getNumElements(); i < e; i++)
|
|
NumElements += getElementCountRec(Module, T.getTupleElementType(i), false);
|
|
return NumElements;
|
|
}
|
|
|
|
// If this is the top level of a 'self' value, we flatten structs and classes.
|
|
// Stored properties with tuple types are tracked with independent lifetimes
|
|
// for each of the tuple members.
|
|
if (IsSelfOfNonDelegatingInitializer) {
|
|
// Protocols never have a stored properties.
|
|
if (auto *NTD = T.getNominalOrBoundGenericNominal()) {
|
|
|
|
unsigned NumElements = 0;
|
|
for (auto *VD : NTD->getStoredProperties())
|
|
NumElements += getElementCountRec(Module, T.getFieldType(VD, Module),
|
|
false);
|
|
return NumElements;
|
|
}
|
|
}
|
|
|
|
// Otherwise, it is a single element.
|
|
return 1;
|
|
}
|
|
|
|
|
|
DIMemoryObjectInfo::DIMemoryObjectInfo(SILInstruction *MI) {
|
|
auto &Module = MI->getModule();
|
|
|
|
MemoryInst = MI;
|
|
// Compute the type of the memory object.
|
|
if (auto *ABI = dyn_cast<AllocBoxInst>(MemoryInst)) {
|
|
assert(ABI->getBoxType()->getLayout()->getFields().size() == 1
|
|
&& "analyzing multi-field boxes not implemented");
|
|
MemorySILType =
|
|
ABI->getBoxType()->getFieldType(Module, 0);
|
|
} else if (auto *ASI = dyn_cast<AllocStackInst>(MemoryInst)) {
|
|
MemorySILType = ASI->getElementType();
|
|
} else {
|
|
auto *MUI = cast<MarkUninitializedInst>(MemoryInst);
|
|
MemorySILType = MUI->getType().getObjectType();
|
|
|
|
// If this is a let variable we're initializing, remember this so we don't
|
|
// allow reassignment.
|
|
if (MUI->isVar())
|
|
if (auto *decl = MUI->getLoc().getAsASTNode<VarDecl>())
|
|
IsLet = decl->isLet();
|
|
}
|
|
|
|
// Compute the number of elements to track in this memory object.
|
|
// If this is a 'self' in a delegating initializer, we only track one bit:
|
|
// whether self.init is called or not.
|
|
if (isDelegatingInit()) {
|
|
NumElements = 1;
|
|
return;
|
|
}
|
|
|
|
// If this is a derived class init method for which stored properties are
|
|
// separately initialized, track an element for the super.init call.
|
|
if (isDerivedClassSelfOnly()) {
|
|
NumElements = 1;
|
|
return;
|
|
}
|
|
|
|
// Otherwise, we break down the initializer.
|
|
NumElements = getElementCountRec(Module, MemorySILType, isNonDelegatingInit());
|
|
|
|
// If this is a derived class init method, track an extra element to determine
|
|
// whether super.init has been called at each program point.
|
|
if (isDerivedClassSelf())
|
|
++NumElements;
|
|
}
|
|
|
|
SILInstruction *DIMemoryObjectInfo::getFunctionEntryPoint() const {
|
|
return &*getFunction().begin()->begin();
|
|
}
|
|
|
|
/// Given a symbolic element number, return the type of the element.
|
|
static SILType getElementTypeRec(SILModule &Module, SILType T, unsigned EltNo,
|
|
bool IsSelfOfNonDelegatingInitializer) {
|
|
// If this is a tuple type, walk into it.
|
|
if (CanTupleType TT = T.getAs<TupleType>()) {
|
|
assert(!IsSelfOfNonDelegatingInitializer && "self never has tuple type");
|
|
for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) {
|
|
auto FieldType = T.getTupleElementType(i);
|
|
unsigned NumFieldElements = getElementCountRec(Module, FieldType, false);
|
|
if (EltNo < NumFieldElements)
|
|
return getElementTypeRec(Module, FieldType, EltNo, false);
|
|
EltNo -= NumFieldElements;
|
|
}
|
|
llvm::report_fatal_error("invalid element number");
|
|
}
|
|
|
|
// If this is the top level of a 'self' value, we flatten structs and classes.
|
|
// Stored properties with tuple types are tracked with independent lifetimes
|
|
// for each of the tuple members.
|
|
if (IsSelfOfNonDelegatingInitializer) {
|
|
if (auto *NTD = T.getNominalOrBoundGenericNominal()) {
|
|
for (auto *VD : NTD->getStoredProperties()) {
|
|
auto FieldType = T.getFieldType(VD, Module);
|
|
unsigned NumFieldElements = getElementCountRec(Module, FieldType, false);
|
|
if (EltNo < NumFieldElements)
|
|
return getElementTypeRec(Module, FieldType, EltNo, false);
|
|
EltNo -= NumFieldElements;
|
|
}
|
|
llvm::report_fatal_error("invalid element number");
|
|
}
|
|
}
|
|
|
|
// Otherwise, it is a leaf element.
|
|
assert(EltNo == 0);
|
|
return T;
|
|
}
|
|
|
|
/// getElementTypeRec - Return the swift type of the specified element.
|
|
SILType DIMemoryObjectInfo::getElementType(unsigned EltNo) const {
|
|
auto &Module = MemoryInst->getModule();
|
|
return getElementTypeRec(Module, MemorySILType, EltNo, isNonDelegatingInit());
|
|
}
|
|
|
|
/// computeTupleElementAddress - Given a tuple element number (in the flattened
|
|
/// sense) return a pointer to a leaf element of the specified number.
|
|
SILValue DIMemoryObjectInfo::
|
|
emitElementAddress(unsigned EltNo, SILLocation Loc, SILBuilder &B) const {
|
|
SILValue Ptr = getAddress();
|
|
bool IsSelf = isNonDelegatingInit();
|
|
auto &Module = MemoryInst->getModule();
|
|
|
|
auto PointeeType = MemorySILType;
|
|
|
|
while (1) {
|
|
// If we have a tuple, flatten it.
|
|
if (CanTupleType TT = PointeeType.getAs<TupleType>()) {
|
|
assert(!IsSelf && "self never has tuple type");
|
|
|
|
// Figure out which field we're walking into.
|
|
unsigned FieldNo = 0;
|
|
for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) {
|
|
auto EltTy = PointeeType.getTupleElementType(i);
|
|
unsigned NumSubElt = getElementCountRec(Module, EltTy, false);
|
|
if (EltNo < NumSubElt) {
|
|
Ptr = B.createTupleElementAddr(Loc, Ptr, FieldNo);
|
|
PointeeType = EltTy;
|
|
break;
|
|
}
|
|
|
|
EltNo -= NumSubElt;
|
|
++FieldNo;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// If this is the top level of a 'self' value, we flatten structs and
|
|
// classes. Stored properties with tuple types are tracked with independent
|
|
// lifetimes for each of the tuple members.
|
|
if (IsSelf) {
|
|
if (auto *NTD = PointeeType.getNominalOrBoundGenericNominal()) {
|
|
if (isa<ClassDecl>(NTD) && Ptr->getType().isAddress())
|
|
Ptr = B.createLoad(Loc, Ptr, LoadOwnershipQualifier::Unqualified);
|
|
for (auto *VD : NTD->getStoredProperties()) {
|
|
auto FieldType = PointeeType.getFieldType(VD, Module);
|
|
unsigned NumFieldElements = getElementCountRec(Module, FieldType, false);
|
|
if (EltNo < NumFieldElements) {
|
|
if (isa<StructDecl>(NTD))
|
|
Ptr = B.createStructElementAddr(Loc, Ptr, VD);
|
|
else {
|
|
assert(isa<ClassDecl>(NTD));
|
|
Ptr = B.createRefElementAddr(Loc, Ptr, VD);
|
|
}
|
|
|
|
PointeeType = FieldType;
|
|
IsSelf = false;
|
|
break;
|
|
}
|
|
EltNo -= NumFieldElements;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Have we gotten to our leaf element?
|
|
assert(EltNo == 0 && "Element count problem");
|
|
return Ptr;
|
|
}
|
|
}
|
|
|
|
|
|
/// Push the symbolic path name to the specified element number onto the
|
|
/// specified std::string.
|
|
static void getPathStringToElementRec(SILModule &Module, SILType T,
|
|
unsigned EltNo, std::string &Result) {
|
|
if (CanTupleType TT = T.getAs<TupleType>()) {
|
|
unsigned FieldNo = 0;
|
|
for (unsigned i = 0, e = TT->getNumElements(); i < e; i++) {
|
|
auto Field = TT->getElement(i);
|
|
SILType FieldTy = T.getTupleElementType(i);
|
|
unsigned NumFieldElements = getElementCountRec(Module, FieldTy, false);
|
|
|
|
if (EltNo < NumFieldElements) {
|
|
Result += '.';
|
|
if (Field.hasName())
|
|
Result += Field.getName().str();
|
|
else
|
|
Result += llvm::utostr(FieldNo);
|
|
return getPathStringToElementRec(Module, FieldTy, EltNo, Result);
|
|
}
|
|
|
|
EltNo -= NumFieldElements;
|
|
|
|
++FieldNo;
|
|
}
|
|
llvm_unreachable("Element number is out of range for this type!");
|
|
}
|
|
|
|
// Otherwise, there are no subelements.
|
|
assert(EltNo == 0 && "Element count problem");
|
|
}
|
|
|
|
ValueDecl *DIMemoryObjectInfo::
|
|
getPathStringToElement(unsigned Element, std::string &Result) const {
|
|
auto &Module = MemoryInst->getModule();
|
|
|
|
if (isAnyInitSelf())
|
|
Result = "self";
|
|
else if (ValueDecl *VD =
|
|
dyn_cast_or_null<ValueDecl>(getLoc().getAsASTNode<Decl>()))
|
|
Result = VD->getName().str();
|
|
else
|
|
Result = "<unknown>";
|
|
|
|
|
|
// If this is indexing into a field of 'self', look it up.
|
|
if (isNonDelegatingInit() && !isDerivedClassSelfOnly()) {
|
|
if (auto *NTD = MemorySILType.getNominalOrBoundGenericNominal()) {
|
|
for (auto *VD : NTD->getStoredProperties()) {
|
|
auto FieldType = MemorySILType.getFieldType(VD, Module);
|
|
unsigned NumFieldElements = getElementCountRec(Module, FieldType, false);
|
|
if (Element < NumFieldElements) {
|
|
Result += '.';
|
|
Result += VD->getName().str();
|
|
getPathStringToElementRec(Module, FieldType, Element, Result);
|
|
return VD;
|
|
}
|
|
Element -= NumFieldElements;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the path through a tuple, if relevant.
|
|
getPathStringToElementRec(Module, MemorySILType, Element, Result);
|
|
|
|
// If we are analyzing a variable, we can generally get the decl associated
|
|
// with it.
|
|
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
|
|
if (MUI->isVar())
|
|
return MUI->getLoc().getAsASTNode<VarDecl>();
|
|
|
|
// Otherwise, we can't.
|
|
return nullptr;
|
|
}
|
|
|
|
/// If the specified value is a 'let' property in an initializer, return true.
|
|
bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const {
|
|
// If we aren't representing 'self' in a non-delegating initializer, then we
|
|
// can't have 'let' properties.
|
|
if (!isNonDelegatingInit()) return IsLet;
|
|
|
|
auto &Module = MemoryInst->getModule();
|
|
|
|
if (auto *NTD = MemorySILType.getNominalOrBoundGenericNominal()) {
|
|
for (auto *VD : NTD->getStoredProperties()) {
|
|
auto FieldType = MemorySILType.getFieldType(VD, Module);
|
|
unsigned NumFieldElements = getElementCountRec(Module, FieldType, false);
|
|
if (Element < NumFieldElements)
|
|
return VD->isLet();
|
|
Element -= NumFieldElements;
|
|
}
|
|
}
|
|
|
|
// Otherwise, we miscounted elements?
|
|
assert(Element == 0 && "Element count problem");
|
|
return false;
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DIMemoryUse Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
/// onlyTouchesTrivialElements - Return true if all of the accessed elements
|
|
/// have trivial type.
|
|
bool DIMemoryUse::
|
|
onlyTouchesTrivialElements(const DIMemoryObjectInfo &MI) const {
|
|
auto &Module = Inst->getModule();
|
|
|
|
for (unsigned i = FirstElement, e = i+NumElements; i != e; ++i) {
|
|
// Skip 'super.init' bit
|
|
if (i == MI.getNumMemoryElements())
|
|
return false;
|
|
|
|
auto EltTy = MI.getElementType(i);
|
|
if (!EltTy.isTrivial(Module))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Scalarization Logic
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Given a pointer to a tuple type, compute the addresses of each element and
|
|
/// add them to the ElementAddrs vector.
|
|
static void getScalarizedElementAddresses(SILValue Pointer, SILBuilder &B,
|
|
SILLocation Loc,
|
|
SmallVectorImpl<SILValue> &ElementAddrs) {
|
|
TupleType *TT = Pointer->getType().castTo<TupleType>();
|
|
for (auto Index : indices(TT->getElements())) {
|
|
ElementAddrs.push_back(B.createTupleElementAddr(Loc, Pointer, Index));
|
|
}
|
|
}
|
|
|
|
/// Given an RValue of aggregate type, compute the values of the elements by
|
|
/// emitting a series of tuple_element instructions.
|
|
static void getScalarizedElements(SILValue V,
|
|
SmallVectorImpl<SILValue> &ElementVals,
|
|
SILLocation Loc, SILBuilder &B) {
|
|
TupleType *TT = V->getType().castTo<TupleType>();
|
|
for (auto Index : indices(TT->getElements())) {
|
|
ElementVals.push_back(B.emitTupleExtract(Loc, V, Index));
|
|
}
|
|
}
|
|
|
|
|
|
/// Scalarize a load down to its subelements. If NewLoads is specified, this
|
|
/// can return the newly generated sub-element loads.
|
|
static SILValue scalarizeLoad(LoadInst *LI,
|
|
SmallVectorImpl<SILValue> &ElementAddrs) {
|
|
SILBuilderWithScope B(LI);
|
|
SmallVector<SILValue, 4> ElementTmps;
|
|
|
|
for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) {
|
|
auto *SubLI = B.createLoad(LI->getLoc(), ElementAddrs[i],
|
|
LoadOwnershipQualifier::Unqualified);
|
|
ElementTmps.push_back(SubLI);
|
|
}
|
|
|
|
if (LI->getType().is<TupleType>())
|
|
return B.createTuple(LI->getLoc(), LI->getType(), ElementTmps);
|
|
return B.createStruct(LI->getLoc(), LI->getType(), ElementTmps);
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ElementUseCollector Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
class ElementUseCollector {
|
|
SILModule &Module;
|
|
const DIMemoryObjectInfo &TheMemory;
|
|
SmallVectorImpl<DIMemoryUse> &Uses;
|
|
SmallVectorImpl<TermInst*> &FailableInits;
|
|
SmallVectorImpl<SILInstruction*> &Releases;
|
|
|
|
/// This is true if definite initialization has finished processing assign
|
|
/// and other ambiguous instructions into init vs assign classes.
|
|
bool isDefiniteInitFinished;
|
|
|
|
/// IsSelfOfNonDelegatingInitializer - This is true if we're looking at the
|
|
/// top level of a 'self' variable in a non-delegating init method.
|
|
bool IsSelfOfNonDelegatingInitializer;
|
|
|
|
/// How should address_to_pointer be handled?
|
|
///
|
|
/// In DefiniteInitialization it is considered as an inout parameter to get
|
|
/// diagnostics about passing a let variable to an inout mutable-pointer
|
|
/// argument.
|
|
/// In PredictableMemOpt it is considered as an escape point to be
|
|
/// conservative.
|
|
bool TreatAddressToPointerAsInout;
|
|
|
|
/// When walking the use list, if we index into a struct element, keep track
|
|
/// of this, so that any indexes into tuple subelements don't affect the
|
|
/// element we attribute an access to.
|
|
bool InStructSubElement = false;
|
|
|
|
/// When walking the use list, if we index into an enum slice, keep track
|
|
/// of this.
|
|
bool InEnumSubElement = false;
|
|
public:
|
|
ElementUseCollector(const DIMemoryObjectInfo &TheMemory,
|
|
SmallVectorImpl<DIMemoryUse> &Uses,
|
|
SmallVectorImpl<TermInst*> &FailableInits,
|
|
SmallVectorImpl<SILInstruction*> &Releases,
|
|
bool isDefiniteInitFinished,
|
|
bool TreatAddressToPointerAsInout)
|
|
: Module(TheMemory.MemoryInst->getModule()),
|
|
TheMemory(TheMemory), Uses(Uses),
|
|
FailableInits(FailableInits), Releases(Releases),
|
|
isDefiniteInitFinished(isDefiniteInitFinished),
|
|
TreatAddressToPointerAsInout(TreatAddressToPointerAsInout) {
|
|
}
|
|
|
|
/// This is the main entry point for the use walker. It collects uses from
|
|
/// the address and the refcount result of the allocation.
|
|
void collectFrom() {
|
|
IsSelfOfNonDelegatingInitializer =
|
|
TheMemory.isNonDelegatingInit();
|
|
|
|
// If this is a delegating initializer, collect uses specially.
|
|
if (TheMemory.isDelegatingInit()) {
|
|
if (TheMemory.getType()->hasReferenceSemantics())
|
|
collectDelegatingClassInitSelfUses();
|
|
else
|
|
collectDelegatingValueTypeInitSelfUses();
|
|
} else if (IsSelfOfNonDelegatingInitializer &&
|
|
TheMemory.getType()->getClassOrBoundGenericClass() != nullptr) {
|
|
if (TheMemory.isDerivedClassSelfOnly())
|
|
collectDelegatingClassInitSelfUses();
|
|
else
|
|
// If this is a class pointer, we need to look through
|
|
// ref_element_addrs.
|
|
collectClassSelfUses();
|
|
} else {
|
|
if (auto *ABI = TheMemory.getContainer())
|
|
collectContainerUses(ABI);
|
|
else
|
|
collectUses(TheMemory.getAddress(), 0);
|
|
}
|
|
|
|
if (!isa<MarkUninitializedInst>(TheMemory.MemoryInst)) {
|
|
// Collect information about the retain count result as well.
|
|
for (auto UI : TheMemory.MemoryInst->getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// If this is a release or dealloc_stack, then remember it as such.
|
|
if (isa<StrongReleaseInst>(User) || isa<DeallocStackInst>(User) ||
|
|
isa<DeallocBoxInst>(User)) {
|
|
Releases.push_back(User);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
void collectUses(SILValue Pointer, unsigned BaseEltNo);
|
|
void collectContainerUses(AllocBoxInst *ABI);
|
|
void recordFailureBB(TermInst *TI, SILBasicBlock *BB);
|
|
void recordFailableInitCall(SILInstruction *I);
|
|
void collectClassSelfUses();
|
|
void collectDelegatingClassInitSelfUses();
|
|
void collectDelegatingValueTypeInitSelfUses();
|
|
void collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType,
|
|
llvm::SmallDenseMap<VarDecl*, unsigned> &EN);
|
|
|
|
void addElementUses(unsigned BaseEltNo, SILType UseTy,
|
|
SILInstruction *User, DIUseKind Kind);
|
|
void collectTupleElementUses(TupleElementAddrInst *TEAI,
|
|
unsigned BaseEltNo);
|
|
void collectStructElementUses(StructElementAddrInst *SEAI,
|
|
unsigned BaseEltNo);
|
|
void collectDelegatingClassInitSelfLoadUses(MarkUninitializedInst *MUI,
|
|
LoadInst *LI);
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// addElementUses - An operation (e.g. load, store, inout use, etc) on a value
|
|
/// acts on all of the aggregate elements in that value. For example, a load
|
|
/// of $*(Int,Int) is a use of both Int elements of the tuple. This is a helper
|
|
/// to keep the Uses data structure up to date for aggregate uses.
|
|
void ElementUseCollector::addElementUses(unsigned BaseEltNo, SILType UseTy,
|
|
SILInstruction *User, DIUseKind Kind) {
|
|
// If we're in a subelement of a struct or enum, just mark the struct, not
|
|
// things that come after it in a parent tuple.
|
|
unsigned NumElements = 1;
|
|
if (TheMemory.NumElements != 1 && !InStructSubElement && !InEnumSubElement)
|
|
NumElements = getElementCountRec(Module, UseTy,
|
|
IsSelfOfNonDelegatingInitializer);
|
|
|
|
Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, NumElements));
|
|
}
|
|
|
|
/// Given a tuple_element_addr or struct_element_addr, compute the new
|
|
/// BaseEltNo implicit in the selected member, and recursively add uses of
|
|
/// the instruction.
|
|
void ElementUseCollector::
|
|
collectTupleElementUses(TupleElementAddrInst *TEAI, unsigned BaseEltNo) {
|
|
|
|
// If we're walking into a tuple within a struct or enum, don't adjust the
|
|
// BaseElt. The uses hanging off the tuple_element_addr are going to be
|
|
// counted as uses of the struct or enum itself.
|
|
if (InStructSubElement || InEnumSubElement)
|
|
return collectUses(TEAI, BaseEltNo);
|
|
|
|
assert(!IsSelfOfNonDelegatingInitializer && "self doesn't have tuple type");
|
|
|
|
// tuple_element_addr P, 42 indexes into the current tuple element.
|
|
// Recursively process its uses with the adjusted element number.
|
|
unsigned FieldNo = TEAI->getFieldNo();
|
|
auto T = TEAI->getOperand()->getType();
|
|
if (T.is<TupleType>()) {
|
|
for (unsigned i = 0; i != FieldNo; ++i) {
|
|
SILType EltTy = T.getTupleElementType(i);
|
|
BaseEltNo += getElementCountRec(Module, EltTy, false);
|
|
}
|
|
}
|
|
|
|
collectUses(TEAI, BaseEltNo);
|
|
}
|
|
|
|
void ElementUseCollector::collectStructElementUses(StructElementAddrInst *SEAI,
|
|
unsigned BaseEltNo) {
|
|
// Generally, we set the "InStructSubElement" flag and recursively process
|
|
// the uses so that we know that we're looking at something within the
|
|
// current element.
|
|
if (!IsSelfOfNonDelegatingInitializer) {
|
|
llvm::SaveAndRestore<bool> X(InStructSubElement, true);
|
|
collectUses(SEAI, BaseEltNo);
|
|
return;
|
|
}
|
|
|
|
// If this is the top level of 'self' in an init method, we treat each
|
|
// element of the struct as an element to be analyzed independently.
|
|
llvm::SaveAndRestore<bool> X(IsSelfOfNonDelegatingInitializer, false);
|
|
|
|
for (auto *VD : SEAI->getStructDecl()->getStoredProperties()) {
|
|
if (SEAI->getField() == VD)
|
|
break;
|
|
|
|
auto FieldType = SEAI->getOperand()->getType().getFieldType(VD, Module);
|
|
BaseEltNo += getElementCountRec(Module, FieldType, false);
|
|
}
|
|
|
|
collectUses(SEAI, BaseEltNo);
|
|
}
|
|
|
|
void ElementUseCollector::collectContainerUses(AllocBoxInst *ABI) {
|
|
for (Operand *UI : ABI->getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// Deallocations and retain/release don't affect the value directly.
|
|
if (isa<DeallocBoxInst>(User))
|
|
continue;
|
|
if (isa<StrongRetainInst>(User))
|
|
continue;
|
|
if (isa<StrongReleaseInst>(User))
|
|
continue;
|
|
|
|
if (auto project = dyn_cast<ProjectBoxInst>(User)) {
|
|
collectUses(User, project->getFieldIndex());
|
|
continue;
|
|
}
|
|
|
|
// Other uses of the container are considered escapes of the values.
|
|
for (unsigned field : indices(ABI->getBoxType()->getLayout()->getFields()))
|
|
addElementUses(field,
|
|
ABI->getBoxType()->getFieldType(ABI->getModule(), field),
|
|
User, DIUseKind::Escape);
|
|
}
|
|
}
|
|
|
|
/// Return the underlying accessed pointer value. This peeks through
|
|
/// begin_access patterns such as:
|
|
///
|
|
/// %mark = mark_uninitialized [rootself] %alloc : $*T
|
|
/// %access = begin_access [modify] [unknown] %mark : $*T
|
|
/// apply %f(%access) : $(@inout T) -> ()
|
|
static SILValue getAccessedPointer(SILValue Pointer) {
|
|
if (auto *Access = dyn_cast<BeginAccessInst>(Pointer))
|
|
return Access->getSource();
|
|
|
|
return Pointer;
|
|
}
|
|
|
|
/// Returns true when the instruction represents added instrumentation for
|
|
/// run-time sanitizers.
|
|
static bool isSanitizerInstrumentation(SILInstruction *Instruction,
|
|
ASTContext &Ctx) {
|
|
auto *BI = dyn_cast<BuiltinInst>(Instruction);
|
|
if (!BI)
|
|
return false;
|
|
|
|
Identifier Name = BI->getName();
|
|
if (Name == Ctx.getIdentifier("tsanInoutAccess"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) {
|
|
assert(Pointer->getType().isAddress() &&
|
|
"Walked through the pointer to the value?");
|
|
SILType PointeeType = Pointer->getType().getObjectType();
|
|
|
|
/// This keeps track of instructions in the use list that touch multiple tuple
|
|
/// elements and should be scalarized. This is done as a second phase to
|
|
/// avoid invalidating the use iterator.
|
|
///
|
|
SmallVector<SILInstruction*, 4> UsesToScalarize;
|
|
|
|
for (auto UI : Pointer->getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// struct_element_addr P, #field indexes into the current element.
|
|
if (auto *SEAI = dyn_cast<StructElementAddrInst>(User)) {
|
|
collectStructElementUses(SEAI, BaseEltNo);
|
|
continue;
|
|
}
|
|
|
|
// Instructions that compute a subelement are handled by a helper.
|
|
if (auto *TEAI = dyn_cast<TupleElementAddrInst>(User)) {
|
|
collectTupleElementUses(TEAI, BaseEltNo);
|
|
continue;
|
|
}
|
|
|
|
// Look through begin_access.
|
|
if (isa<BeginAccessInst>(User)) {
|
|
collectUses(User, BaseEltNo);
|
|
continue;
|
|
}
|
|
|
|
// Ignore end_access.
|
|
if (isa<EndAccessInst>(User)) {
|
|
continue;
|
|
}
|
|
|
|
// Loads are a use of the value.
|
|
if (isa<LoadInst>(User)) {
|
|
if (PointeeType.is<TupleType>())
|
|
UsesToScalarize.push_back(User);
|
|
else
|
|
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Load);
|
|
continue;
|
|
}
|
|
|
|
if (isa<LoadWeakInst>(User)) {
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::Load, BaseEltNo, 1));
|
|
continue;
|
|
}
|
|
|
|
// Stores *to* the allocation are writes.
|
|
if ((isa<StoreInst>(User) || isa<AssignInst>(User)) &&
|
|
UI->getOperandNumber() == 1) {
|
|
if (PointeeType.is<TupleType>()) {
|
|
UsesToScalarize.push_back(User);
|
|
continue;
|
|
}
|
|
|
|
// Coming out of SILGen, we assume that raw stores are initializations,
|
|
// unless they have trivial type (which we classify as InitOrAssign).
|
|
DIUseKind Kind;
|
|
if (InStructSubElement)
|
|
Kind = DIUseKind::PartialStore;
|
|
else if (isa<AssignInst>(User))
|
|
Kind = DIUseKind::InitOrAssign;
|
|
else if (PointeeType.isTrivial(User->getModule()))
|
|
Kind = DIUseKind::InitOrAssign;
|
|
else
|
|
Kind = DIUseKind::Initialization;
|
|
|
|
addElementUses(BaseEltNo, PointeeType, User, Kind);
|
|
continue;
|
|
}
|
|
|
|
if (auto SWI = dyn_cast<StoreWeakInst>(User))
|
|
if (UI->getOperandNumber() == 1) {
|
|
DIUseKind Kind;
|
|
if (InStructSubElement)
|
|
Kind = DIUseKind::PartialStore;
|
|
else if (SWI->isInitializationOfDest())
|
|
Kind = DIUseKind::Initialization;
|
|
else if (isDefiniteInitFinished)
|
|
Kind = DIUseKind::Assign;
|
|
else
|
|
Kind = DIUseKind::InitOrAssign;
|
|
Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, 1));
|
|
continue;
|
|
}
|
|
|
|
if (auto SUI = dyn_cast<StoreUnownedInst>(User))
|
|
if (UI->getOperandNumber() == 1) {
|
|
DIUseKind Kind;
|
|
if (InStructSubElement)
|
|
Kind = DIUseKind::PartialStore;
|
|
else if (SUI->isInitializationOfDest())
|
|
Kind = DIUseKind::Initialization;
|
|
else if (isDefiniteInitFinished)
|
|
Kind = DIUseKind::Assign;
|
|
else
|
|
Kind = DIUseKind::InitOrAssign;
|
|
Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, 1));
|
|
continue;
|
|
}
|
|
|
|
if (auto *CAI = dyn_cast<CopyAddrInst>(User)) {
|
|
// If this is a copy of a tuple, we should scalarize it so that we don't
|
|
// have an access that crosses elements.
|
|
if (PointeeType.is<TupleType>()) {
|
|
UsesToScalarize.push_back(CAI);
|
|
continue;
|
|
}
|
|
|
|
// If this is the source of the copy_addr, then this is a load. If it is
|
|
// the destination, then this is an unknown assignment. Note that we'll
|
|
// revisit this instruction and add it to Uses twice if it is both a load
|
|
// and store to the same aggregate.
|
|
DIUseKind Kind;
|
|
if (UI->getOperandNumber() == 0)
|
|
Kind = DIUseKind::Load;
|
|
else if (InStructSubElement)
|
|
Kind = DIUseKind::PartialStore;
|
|
else if (CAI->isInitializationOfDest())
|
|
Kind = DIUseKind::Initialization;
|
|
else if (isDefiniteInitFinished)
|
|
Kind = DIUseKind::Assign;
|
|
else
|
|
Kind = DIUseKind::InitOrAssign;
|
|
|
|
addElementUses(BaseEltNo, PointeeType, User, Kind);
|
|
continue;
|
|
}
|
|
|
|
// The apply instruction does not capture the pointer when it is passed
|
|
// through 'inout' arguments or for indirect returns. InOut arguments are
|
|
// treated as uses and may-store's, but an indirect return is treated as a
|
|
// full store.
|
|
//
|
|
// Note that partial_apply instructions always close over their argument.
|
|
//
|
|
if (auto *Apply = dyn_cast<ApplyInst>(User)) {
|
|
auto substConv = Apply->getSubstCalleeConv();
|
|
unsigned ArgumentNumber = UI->getOperandNumber()-1;
|
|
|
|
// If this is an out-parameter, it is like a store.
|
|
unsigned NumIndirectResults = substConv.getNumIndirectSILResults();
|
|
if (ArgumentNumber < NumIndirectResults) {
|
|
assert(!InStructSubElement && "We're initializing sub-members?");
|
|
addElementUses(BaseEltNo, PointeeType, User,
|
|
DIUseKind::Initialization);
|
|
continue;
|
|
|
|
// Otherwise, adjust the argument index.
|
|
} else {
|
|
ArgumentNumber -= NumIndirectResults;
|
|
}
|
|
|
|
auto ParamConvention =
|
|
substConv.getParameters()[ArgumentNumber].getConvention();
|
|
|
|
switch (ParamConvention) {
|
|
case ParameterConvention::Direct_Owned:
|
|
case ParameterConvention::Direct_Unowned:
|
|
case ParameterConvention::Direct_Guaranteed:
|
|
llvm_unreachable("address value passed to indirect parameter");
|
|
|
|
// If this is an in-parameter, it is like a load.
|
|
case ParameterConvention::Indirect_In:
|
|
case ParameterConvention::Indirect_In_Guaranteed:
|
|
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn);
|
|
continue;
|
|
|
|
// If this is an @inout parameter, it is like both a load and store.
|
|
case ParameterConvention::Indirect_Inout:
|
|
case ParameterConvention::Indirect_InoutAliasable: {
|
|
// If we're in the initializer for a struct, and this is a call to a
|
|
// mutating method, we model that as an escape of self. If an
|
|
// individual sub-member is passed as inout, then we model that as an
|
|
// inout use.
|
|
auto Kind = DIUseKind::InOutUse;
|
|
if ((TheMemory.isStructInitSelf() || TheMemory.isProtocolInitSelf())
|
|
&& getAccessedPointer(Pointer) == TheMemory.getAddress())
|
|
Kind = DIUseKind::Escape;
|
|
|
|
addElementUses(BaseEltNo, PointeeType, User, Kind);
|
|
continue;
|
|
}
|
|
}
|
|
llvm_unreachable("bad parameter convention");
|
|
}
|
|
|
|
if (isa<AddressToPointerInst>(User) && TreatAddressToPointerAsInout) {
|
|
// address_to_pointer is a mutable escape, which we model as an inout use.
|
|
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutUse);
|
|
continue;
|
|
}
|
|
|
|
|
|
// init_enum_data_addr is treated like a tuple_element_addr or other instruction
|
|
// that is looking into the memory object (i.e., the memory object needs to
|
|
// be explicitly initialized by a copy_addr or some other use of the
|
|
// projected address).
|
|
if (isa<InitEnumDataAddrInst>(User)) {
|
|
assert(!InStructSubElement &&
|
|
"init_enum_data_addr shouldn't apply to struct subelements");
|
|
// Keep track of the fact that we're inside of an enum. This informs our
|
|
// recursion that tuple stores are not scalarized outside, and that stores
|
|
// should not be treated as partial stores.
|
|
llvm::SaveAndRestore<bool> X(InEnumSubElement, true);
|
|
collectUses(User, BaseEltNo);
|
|
continue;
|
|
}
|
|
|
|
// init_existential_addr is modeled as an initialization store.
|
|
if (isa<InitExistentialAddrInst>(User)) {
|
|
assert(!InStructSubElement &&
|
|
"init_existential_addr should not apply to struct subelements");
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::Initialization,
|
|
BaseEltNo, 1));
|
|
continue;
|
|
}
|
|
|
|
// inject_enum_addr is modeled as an initialization store.
|
|
if (isa<InjectEnumAddrInst>(User)) {
|
|
assert(!InStructSubElement &&
|
|
"inject_enum_addr the subelement of a struct unless in a ctor");
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::Initialization,
|
|
BaseEltNo, 1));
|
|
continue;
|
|
}
|
|
|
|
// open_existential_addr is a use of the protocol value,
|
|
// so it is modeled as a load.
|
|
if (isa<OpenExistentialAddrInst>(User)) {
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::Load, BaseEltNo, 1));
|
|
// TODO: Is it safe to ignore all uses of the open_existential_addr?
|
|
continue;
|
|
}
|
|
|
|
// We model destroy_addr as a release of the entire value.
|
|
if (isa<DestroyAddrInst>(User)) {
|
|
Releases.push_back(User);
|
|
continue;
|
|
}
|
|
|
|
if (isa<DeallocStackInst>(User)) {
|
|
continue;
|
|
}
|
|
|
|
// Sanitizer instrumentation is not user visible, so it should not
|
|
// count as a use and must not affect compile-time diagnostics.
|
|
if (isSanitizerInstrumentation(User, Module.getASTContext()))
|
|
continue;
|
|
|
|
// Otherwise, the use is something complicated, it escapes.
|
|
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Escape);
|
|
}
|
|
|
|
// Now that we've walked all of the immediate uses, scalarize any operations
|
|
// working on tuples if we need to for canonicalization or analysis reasons.
|
|
if (!UsesToScalarize.empty()) {
|
|
SILInstruction *PointerInst = cast<SILInstruction>(Pointer);
|
|
SmallVector<SILValue, 4> ElementAddrs;
|
|
SILBuilderWithScope AddrBuilder(++SILBasicBlock::iterator(PointerInst),
|
|
PointerInst);
|
|
getScalarizedElementAddresses(Pointer, AddrBuilder, PointerInst->getLoc(),
|
|
ElementAddrs);
|
|
|
|
SmallVector<SILValue, 4> ElementTmps;
|
|
for (auto *User : UsesToScalarize) {
|
|
ElementTmps.clear();
|
|
|
|
DEBUG(llvm::errs() << " *** Scalarizing: " << *User << "\n");
|
|
|
|
// Scalarize LoadInst
|
|
if (auto *LI = dyn_cast<LoadInst>(User)) {
|
|
SILValue Result = scalarizeLoad(LI, ElementAddrs);
|
|
LI->replaceAllUsesWith(Result);
|
|
LI->eraseFromParent();
|
|
continue;
|
|
}
|
|
|
|
// Scalarize AssignInst
|
|
if (auto *AI = dyn_cast<AssignInst>(User)) {
|
|
SILBuilderWithScope B(User, AI);
|
|
getScalarizedElements(AI->getOperand(0), ElementTmps, AI->getLoc(), B);
|
|
|
|
for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i)
|
|
B.createAssign(AI->getLoc(), ElementTmps[i], ElementAddrs[i]);
|
|
AI->eraseFromParent();
|
|
continue;
|
|
}
|
|
|
|
// Scalarize StoreInst
|
|
if (auto *SI = dyn_cast<StoreInst>(User)) {
|
|
SILBuilderWithScope B(User, SI);
|
|
getScalarizedElements(SI->getOperand(0), ElementTmps, SI->getLoc(), B);
|
|
|
|
for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i)
|
|
B.createStore(SI->getLoc(), ElementTmps[i], ElementAddrs[i],
|
|
StoreOwnershipQualifier::Unqualified);
|
|
SI->eraseFromParent();
|
|
continue;
|
|
}
|
|
|
|
// Scalarize CopyAddrInst.
|
|
auto *CAI = cast<CopyAddrInst>(User);
|
|
SILBuilderWithScope B(User, CAI);
|
|
|
|
// Determine if this is a copy *from* or *to* "Pointer".
|
|
if (CAI->getSrc() == Pointer) {
|
|
// Copy from pointer.
|
|
getScalarizedElementAddresses(CAI->getDest(), B, CAI->getLoc(),
|
|
ElementTmps);
|
|
for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i)
|
|
B.createCopyAddr(CAI->getLoc(), ElementAddrs[i], ElementTmps[i],
|
|
CAI->isTakeOfSrc(), CAI->isInitializationOfDest());
|
|
|
|
} else {
|
|
getScalarizedElementAddresses(CAI->getSrc(), B, CAI->getLoc(),
|
|
ElementTmps);
|
|
for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i)
|
|
B.createCopyAddr(CAI->getLoc(), ElementTmps[i], ElementAddrs[i],
|
|
CAI->isTakeOfSrc(), CAI->isInitializationOfDest());
|
|
}
|
|
CAI->eraseFromParent();
|
|
}
|
|
|
|
// Now that we've scalarized some stuff, recurse down into the newly created
|
|
// element address computations to recursively process it. This can cause
|
|
// further scalarization.
|
|
for (auto EltPtr : ElementAddrs)
|
|
collectTupleElementUses(cast<TupleElementAddrInst>(EltPtr), BaseEltNo);
|
|
}
|
|
}
|
|
|
|
/// recordFailureBB - we have to detect if the self box contents were consumed.
|
|
/// Do this by checking for a store into the self box in the success branch.
|
|
/// Once we rip this out of SILGen, DI will be able to figure this out in a
|
|
/// more logical manner.
|
|
void ElementUseCollector::recordFailureBB(TermInst *TI,
|
|
SILBasicBlock *BB) {
|
|
for (auto &II : *BB)
|
|
if (auto *SI = dyn_cast<StoreInst>(&II)) {
|
|
if (SI->getDest() == TheMemory.MemoryInst) {
|
|
FailableInits.push_back(TI);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// recordFailableInitCall - If I is a call of a throwing or failable
|
|
/// initializer, add the actual conditional (try_apply or cond_br) to
|
|
/// the set for dataflow analysis.
|
|
void ElementUseCollector::recordFailableInitCall(SILInstruction *I) {
|
|
// If we have a store to self inside the normal BB, we have a 'real'
|
|
// try_apply. Otherwise, this is a 'try? self.init()' or similar,
|
|
// and there is a store after.
|
|
if (auto *TAI = dyn_cast<TryApplyInst>(I)) {
|
|
recordFailureBB(TAI, TAI->getNormalBB());
|
|
return;
|
|
}
|
|
|
|
if (auto *AI = dyn_cast<ApplyInst>(I)) {
|
|
// See if this is an optional initializer.
|
|
for (auto UI : AI->getUses()) {
|
|
SILInstruction *User = UI->getUser();
|
|
|
|
if (!isa<SelectEnumInst>(User) && !isa<SelectEnumAddrInst>(User))
|
|
continue;
|
|
|
|
if (!User->hasOneUse())
|
|
continue;
|
|
|
|
User = User->use_begin()->getUser();
|
|
if (auto *CBI = dyn_cast<CondBranchInst>(User)) {
|
|
recordFailureBB(CBI, CBI->getTrueBB());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// collectClassSelfUses - Collect all the uses of a 'self' pointer in a class
|
|
/// constructor. The memory object has class type.
|
|
void ElementUseCollector::collectClassSelfUses() {
|
|
assert(IsSelfOfNonDelegatingInitializer &&
|
|
TheMemory.getType()->getClassOrBoundGenericClass() != nullptr);
|
|
|
|
// For efficiency of lookup below, compute a mapping of the local ivars in the
|
|
// class to their element number.
|
|
llvm::SmallDenseMap<VarDecl*, unsigned> EltNumbering;
|
|
|
|
{
|
|
SILType T = TheMemory.MemorySILType;
|
|
auto *NTD = T.getNominalOrBoundGenericNominal();
|
|
unsigned NumElements = 0;
|
|
for (auto *VD : NTD->getStoredProperties()) {
|
|
EltNumbering[VD] = NumElements;
|
|
NumElements += getElementCountRec(Module, T.getFieldType(VD, Module),
|
|
false);
|
|
}
|
|
}
|
|
|
|
// If we are looking at the init method for a root class, just walk the
|
|
// MUI use-def chain directly to find our uses.
|
|
auto *MUI = cast<MarkUninitializedInst>(TheMemory.MemoryInst);
|
|
if (MUI->getKind() == MarkUninitializedInst::RootSelf) {
|
|
collectClassSelfUses(TheMemory.getAddress(), TheMemory.MemorySILType,
|
|
EltNumbering);
|
|
return;
|
|
}
|
|
|
|
// Okay, given that we have a proper setup, we walk the use chains of the self
|
|
// box to find any accesses to it. The possible uses are one of:
|
|
// 1) The initialization store (TheStore).
|
|
// 2) Loads of the box, which have uses of self hanging off of them.
|
|
// 3) An assign to the box, which happens at super.init.
|
|
// 4) Potential escapes after super.init, if self is closed over.
|
|
// Handle each of these in turn.
|
|
//
|
|
for (auto UI : MUI->getUses()) {
|
|
SILInstruction *User = UI->getUser();
|
|
|
|
// Stores to self are initializations store or the rebind of self as
|
|
// part of the super.init call. Ignore both of these.
|
|
if (isa<StoreInst>(User) && UI->getOperandNumber() == 1)
|
|
continue;
|
|
|
|
// Loads of the box produce self, so collect uses from them.
|
|
if (auto *LI = dyn_cast<LoadInst>(User)) {
|
|
collectClassSelfUses(LI, TheMemory.MemorySILType, EltNumbering);
|
|
continue;
|
|
}
|
|
|
|
// destroyaddr on the box is load+release, which is treated as a release.
|
|
if (isa<DestroyAddrInst>(User) || isa<StrongReleaseInst>(User)) {
|
|
Releases.push_back(User);
|
|
continue;
|
|
}
|
|
|
|
// Ignore the deallocation of the stack box. Its contents will be
|
|
// uninitialized by the point it executes.
|
|
if (isa<DeallocStackInst>(User))
|
|
continue;
|
|
|
|
// We can safely handle anything else as an escape. They should all happen
|
|
// after super.init is invoked. As such, all elements must be initialized
|
|
// and super.init must be called.
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::Load,
|
|
0, TheMemory.NumElements));
|
|
}
|
|
}
|
|
|
|
static void
|
|
collectBorrowedSuperUses(UpcastInst *Inst,
|
|
llvm::SmallVectorImpl<UpcastInst *> &UpcastUsers) {
|
|
for (auto *Use : Inst->getUses()) {
|
|
if (auto *URCI = dyn_cast<UncheckedRefCastInst>(Use->getUser())) {
|
|
for (auto *InnerUse : URCI->getUses()) {
|
|
if (auto *InnerUpcastUser = dyn_cast<UpcastInst>(InnerUse->getUser())) {
|
|
UpcastUsers.push_back(InnerUpcastUser);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// isSuperInitUse - If this "upcast" is part of a call to super.init, return
|
|
/// the Apply instruction for the call, otherwise return null.
|
|
static SILInstruction *isSuperInitUse(UpcastInst *Inst) {
|
|
|
|
// "Inst" is an Upcast instruction. Check to see if it is used by an apply
|
|
// that came from a call to super.init.
|
|
for (auto *UI : Inst->getUses()) {
|
|
auto *User = UI->getUser();
|
|
// If this used by another upcast instruction, recursively handle it, we may
|
|
// have a multiple upcast chain.
|
|
if (auto *UCIU = dyn_cast<UpcastInst>(User))
|
|
if (auto *subAI = isSuperInitUse(UCIU))
|
|
return subAI;
|
|
|
|
// The call to super.init has to either be an apply or a try_apply.
|
|
if (!isa<FullApplySite>(User))
|
|
continue;
|
|
|
|
auto *LocExpr = User->getLoc().getAsASTNode<ApplyExpr>();
|
|
if (!LocExpr) {
|
|
// If we're reading a .sil file, treat a call to "superinit" as a
|
|
// super.init call as a hack to allow us to write testcases.
|
|
auto *AI = dyn_cast<ApplyInst>(User);
|
|
if (AI && User->getLoc().isSILFile())
|
|
if (auto *Fn = AI->getReferencedFunction())
|
|
if (Fn->getName() == "superinit")
|
|
return User;
|
|
continue;
|
|
}
|
|
|
|
// This is a super.init call if structured like this:
|
|
// (call_expr type='SomeClass'
|
|
// (dot_syntax_call_expr type='() -> SomeClass' super
|
|
// (other_constructor_ref_expr implicit decl=SomeClass.init)
|
|
// (super_ref_expr type='SomeClass'))
|
|
// (...some argument...)
|
|
LocExpr = dyn_cast<ApplyExpr>(LocExpr->getFn());
|
|
if (!LocExpr || !isa<OtherConstructorDeclRefExpr>(LocExpr->getFn()))
|
|
continue;
|
|
|
|
if (LocExpr->getArg()->isSuperExpr())
|
|
return User;
|
|
|
|
// Instead of super_ref_expr, we can also get this for inherited delegating
|
|
// initializers:
|
|
|
|
// (derived_to_base_expr implicit type='C'
|
|
// (declref_expr type='D' decl='self'))
|
|
if (auto *DTB = dyn_cast<DerivedToBaseExpr>(LocExpr->getArg()))
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(DTB->getSubExpr()))
|
|
if (DRE->getDecl()->isImplicit() &&
|
|
DRE->getDecl()->getName().str() == "self")
|
|
return User;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// isSelfInitUse - Return true if this apply_inst is a call to self.init.
|
|
static bool isSelfInitUse(SILInstruction *I) {
|
|
// If we're reading a .sil file, treat a call to "selfinit" as a
|
|
// self.init call as a hack to allow us to write testcases.
|
|
if (I->getLoc().isSILFile()) {
|
|
if (auto *AI = dyn_cast<ApplyInst>(I))
|
|
if (auto *Fn = AI->getReferencedFunction())
|
|
if (Fn->getName().startswith("selfinit"))
|
|
return true;
|
|
|
|
// If this is a copy_addr to a delegating self MUI, then we treat it as a
|
|
// self init for the purposes of testcases.
|
|
if (auto *CAI = dyn_cast<CopyAddrInst>(I))
|
|
if (auto *MUI = dyn_cast<MarkUninitializedInst>(CAI->getDest()))
|
|
if (MUI->isDelegatingSelf())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, a self.init call must have location info, and must be an expr
|
|
// to be considered.
|
|
auto *LocExpr = I->getLoc().getAsASTNode<Expr>();
|
|
if (!LocExpr) return false;
|
|
|
|
// If this is a force_value_expr, it might be a self.init()! call, look
|
|
// through it.
|
|
if (auto *FVE = dyn_cast<ForceValueExpr>(LocExpr))
|
|
LocExpr = FVE->getSubExpr();
|
|
|
|
// If we have the rebind_self_in_constructor_expr, then the call is the
|
|
// sub-expression.
|
|
if (auto *RB = dyn_cast<RebindSelfInConstructorExpr>(LocExpr)) {
|
|
LocExpr = RB->getSubExpr();
|
|
// Look through TryExpr or ForceValueExpr, but not both.
|
|
if (auto *TE = dyn_cast<AnyTryExpr>(LocExpr))
|
|
LocExpr = TE->getSubExpr();
|
|
else if (auto *FVE = dyn_cast<ForceValueExpr>(LocExpr))
|
|
LocExpr = FVE->getSubExpr();
|
|
|
|
}
|
|
|
|
// Look through covariant return, if any.
|
|
if (auto CRE = dyn_cast<CovariantReturnConversionExpr>(LocExpr))
|
|
LocExpr = CRE->getSubExpr();
|
|
|
|
// This is a self.init call if structured like this:
|
|
//
|
|
// (call_expr type='SomeClass'
|
|
// (dot_syntax_call_expr type='() -> SomeClass' self
|
|
// (other_constructor_ref_expr implicit decl=SomeClass.init)
|
|
// (decl_ref_expr type='SomeClass', "self"))
|
|
// (...some argument...)
|
|
//
|
|
// Or like this:
|
|
//
|
|
// (call_expr type='SomeClass'
|
|
// (dot_syntax_call_expr type='() -> SomeClass' self
|
|
// (decr_ref_expr implicit decl=SomeClass.init)
|
|
// (decl_ref_expr type='SomeClass', "self"))
|
|
// (...some argument...)
|
|
//
|
|
if (auto *AE = dyn_cast<ApplyExpr>(LocExpr)) {
|
|
if ((AE = dyn_cast<ApplyExpr>(AE->getFn()))) {
|
|
if (isa<OtherConstructorDeclRefExpr>(AE->getFn()))
|
|
return true;
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(AE->getFn()))
|
|
if (auto *CD = dyn_cast<ConstructorDecl>(DRE->getDecl()))
|
|
if (CD->isFactoryInit())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/// Return true if this SILBBArgument is the result of a call to self.init.
|
|
static bool isSelfInitUse(SILArgument *Arg) {
|
|
// We only handle a very simple pattern here where there is a single
|
|
// predecessor to the block, and the predecessor instruction is a try_apply
|
|
// of a throwing delegated init.
|
|
auto *BB = Arg->getParent();
|
|
auto *Pred = BB->getSinglePredecessorBlock();
|
|
|
|
// The two interesting cases are where self.init throws, in which case
|
|
// the argument came from a try_apply, or if self.init is failable,
|
|
// in which case we have a switch_enum.
|
|
if (!Pred ||
|
|
(!isa<TryApplyInst>(Pred->getTerminator()) &&
|
|
!isa<SwitchEnumInst>(Pred->getTerminator())))
|
|
return false;
|
|
|
|
return isSelfInitUse(Pred->getTerminator());
|
|
}
|
|
|
|
/// Returns true if \p Method is a callee of a full apply site that takes in \p
|
|
/// Pointer as an argument. In such a case, we want to ignore the class method
|
|
/// use and allow for the use by the apply inst to take precedence.
|
|
static bool shouldIgnoreClassMethodUseError(
|
|
ClassMethodInst *Method, SILValue Pointer) {
|
|
|
|
// In order to work around use-list ordering issues, if this method is called
|
|
// by an apply site that has I as an argument, we want to process the apply
|
|
// site for errors to emit, not the class method. If we do not obey these
|
|
// conditions, then continue to treat the class method as an escape.
|
|
auto CheckFullApplySite = [&](Operand *Op) -> bool {
|
|
FullApplySite FAS(Op->getUser());
|
|
if (!FAS || (FAS.getCallee() != Method))
|
|
return false;
|
|
return llvm::any_of(
|
|
FAS.getArgumentsWithoutIndirectResults(),
|
|
[&](SILValue Arg) -> bool { return Arg == Pointer; });
|
|
};
|
|
|
|
return llvm::any_of(Method->getUses(), CheckFullApplySite);
|
|
}
|
|
|
|
void ElementUseCollector::
|
|
collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType,
|
|
llvm::SmallDenseMap<VarDecl*, unsigned> &EltNumbering) {
|
|
for (auto UI : ClassPointer->getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// super_method always looks at the metatype for the class, not at any of
|
|
// its stored properties, so it doesn't have any DI requirements.
|
|
if (isa<SuperMethodInst>(User))
|
|
continue;
|
|
|
|
|
|
// ref_element_addr P, #field lookups up a field.
|
|
if (auto *REAI = dyn_cast<RefElementAddrInst>(User)) {
|
|
assert(EltNumbering.count(REAI->getField()) &&
|
|
"ref_element_addr not a local field?");
|
|
// Recursively collect uses of the fields. Note that fields of the class
|
|
// could be tuples, so they may be tracked as independent elements.
|
|
llvm::SaveAndRestore<bool> X(IsSelfOfNonDelegatingInitializer, false);
|
|
collectUses(REAI, EltNumbering[REAI->getField()]);
|
|
continue;
|
|
}
|
|
|
|
// retains of self in class constructors can be ignored since we do not care
|
|
// about the retain that we are producing, but rather the consumer of the
|
|
// retain. This /should/ be true today and will be verified as true in
|
|
// Semantic SIL.
|
|
if (isa<StrongRetainInst>(User)) {
|
|
continue;
|
|
}
|
|
|
|
// releases of self are tracked as a release. In the case of a failing
|
|
// initializer, the release on the exit path needs to cleanup the partially
|
|
// initialized elements.
|
|
if (isa<StrongReleaseInst>(User)) {
|
|
Releases.push_back(User);
|
|
continue;
|
|
}
|
|
|
|
if (auto *Method = dyn_cast<ClassMethodInst>(User)) {
|
|
if (shouldIgnoreClassMethodUseError(Method, ClassPointer)){
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If this is an upcast instruction, it is a conversion of self to the base.
|
|
// This is either part of a super.init sequence, or a general superclass
|
|
// access.
|
|
if (auto *UCI = dyn_cast<UpcastInst>(User))
|
|
if (auto *AI = isSuperInitUse(UCI)) {
|
|
// We remember the applyinst as the super.init site, not the upcast.
|
|
Uses.push_back(DIMemoryUse(AI, DIUseKind::SuperInit,
|
|
0, TheMemory.NumElements));
|
|
recordFailableInitCall(AI);
|
|
|
|
// Now that we know that we have a super.init site, check if our upcast
|
|
// has any borrow users. These used to be represented by a separate
|
|
// load, but now with sil ownership, they are represented as borrows
|
|
// from the same upcast as the super init user upcast.
|
|
llvm::SmallVector<UpcastInst *, 4> ExtraUpcasts;
|
|
collectBorrowedSuperUses(UCI, ExtraUpcasts);
|
|
for (auto *Upcast : ExtraUpcasts) {
|
|
Uses.push_back(
|
|
DIMemoryUse(Upcast, DIUseKind::Load, 0, TheMemory.NumElements));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// If this is an ApplyInst, check to see if this is part of a self.init
|
|
// call in a delegating initializer.
|
|
DIUseKind Kind = DIUseKind::Load;
|
|
if (isa<FullApplySite>(User) && isSelfInitUse(User)) {
|
|
Kind = DIUseKind::SelfInit;
|
|
recordFailableInitCall(User);
|
|
}
|
|
|
|
// If this is a ValueMetatypeInst, this is a simple reference
|
|
// to "type(of:)", which is always fine, even if self is
|
|
// uninitialized.
|
|
if (isa<ValueMetatypeInst>(User))
|
|
continue;
|
|
|
|
// If this is a partial application of self, then this is an escape point
|
|
// for it.
|
|
if (isa<PartialApplyInst>(User))
|
|
Kind = DIUseKind::Escape;
|
|
|
|
Uses.push_back(DIMemoryUse(User, Kind, 0, TheMemory.NumElements));
|
|
}
|
|
}
|
|
|
|
void ElementUseCollector::collectDelegatingClassInitSelfLoadUses(
|
|
MarkUninitializedInst *MUI, LoadInst *LI) {
|
|
|
|
// If we have a load, then this is a use of the box. Look at the uses of
|
|
// the load to find out more information.
|
|
for (auto UI : LI->getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// super_method always looks at the metatype for the class, not at any of
|
|
// its stored properties, so it doesn't have any DI requirements.
|
|
if (isa<SuperMethodInst>(User))
|
|
continue;
|
|
|
|
// We ignore retains of self.
|
|
if (isa<StrongRetainInst>(User))
|
|
continue;
|
|
|
|
// A release of a load from the self box in a class delegating
|
|
// initializer might be releasing an uninitialized self, which requires
|
|
// special processing.
|
|
if (isa<StrongReleaseInst>(User)) {
|
|
Releases.push_back(User);
|
|
continue;
|
|
}
|
|
|
|
if (auto *Method = dyn_cast<ClassMethodInst>(User)) {
|
|
// class_method that refers to an initializing constructor is a method
|
|
// lookup for delegation, which is ignored.
|
|
if (Method->getMember().kind == SILDeclRef::Kind::Initializer)
|
|
continue;
|
|
|
|
/// Returns true if \p Method used by an apply in a way that we know
|
|
/// will cause us to emit a better error.
|
|
if (shouldIgnoreClassMethodUseError(Method, LI))
|
|
continue;
|
|
}
|
|
|
|
// If this is an upcast instruction, it is a conversion of self to the
|
|
// base. This is either part of a super.init sequence, or a general
|
|
// superclass access. We special case super.init calls since they are
|
|
// part of the object lifecycle.
|
|
if (auto *UCI = dyn_cast<UpcastInst>(User)) {
|
|
if (auto *subAI = isSuperInitUse(UCI)) {
|
|
Uses.push_back(DIMemoryUse(subAI, DIUseKind::SuperInit, 0, 1));
|
|
recordFailableInitCall(subAI);
|
|
|
|
// Now that we know that we have a super.init site, check if our upcast
|
|
// has any borrow users. These used to be represented by a separate
|
|
// load, but now with sil ownership, they are represented as borrows
|
|
// from the same upcast as the super init user upcast.
|
|
llvm::SmallVector<UpcastInst *, 4> ExtraUpcasts;
|
|
collectBorrowedSuperUses(UCI, ExtraUpcasts);
|
|
for (auto *Upcast : ExtraUpcasts) {
|
|
Uses.push_back(
|
|
DIMemoryUse(Upcast, DIUseKind::Escape, 0, TheMemory.NumElements));
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We only track two kinds of uses for delegating initializers:
|
|
// calls to self.init, and "other", which we choose to model as escapes.
|
|
// This intentionally ignores all stores, which (if they got emitted as
|
|
// copyaddr or assigns) will eventually get rewritten as assignments
|
|
// (not initializations), which is the right thing to do.
|
|
DIUseKind Kind = DIUseKind::Escape;
|
|
|
|
// If this is an ApplyInst, check to see if this is part of a self.init
|
|
// call in a delegating initializer.
|
|
if (isa<FullApplySite>(User) && isSelfInitUse(User)) {
|
|
Kind = DIUseKind::SelfInit;
|
|
recordFailableInitCall(User);
|
|
}
|
|
|
|
// If this load's value is being stored back into the delegating
|
|
// mark_uninitialized buffer and it is a self init use, skip the
|
|
// use. This is to handle situations where due to usage of a metatype to
|
|
// allocate, we do not actually consume self.
|
|
if (auto *SI = dyn_cast<StoreInst>(User)) {
|
|
if (SI->getDest() == MUI && isSelfInitUse(User)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// A simple reference to "type(of:)" is always fine,
|
|
// even if self is uninitialized.
|
|
if (isa<ValueMetatypeInst>(User)) {
|
|
continue;
|
|
}
|
|
|
|
Uses.push_back(DIMemoryUse(User, Kind, 0, 1));
|
|
}
|
|
}
|
|
|
|
/// collectDelegatingClassInitSelfUses - Collect uses of the self argument in a
|
|
/// delegating-constructor-for-a-class case.
|
|
void ElementUseCollector::collectDelegatingClassInitSelfUses() {
|
|
// When we're analyzing a delegating constructor, we aren't field sensitive at
|
|
// all. Just treat all members of self as uses of the single
|
|
// non-field-sensitive value.
|
|
assert(TheMemory.NumElements == 1 && "delegating inits only have 1 bit");
|
|
auto *MUI = cast<MarkUninitializedInst>(TheMemory.MemoryInst);
|
|
|
|
// We walk the use chains of the self MUI to find any accesses to it. The
|
|
// possible uses are:
|
|
// 1) The initialization store.
|
|
// 2) Loads of the box, which have uses of self hanging off of them.
|
|
// 3) An assign to the box, which happens at super.init.
|
|
// 4) Potential escapes after super.init, if self is closed over.
|
|
// Handle each of these in turn.
|
|
//
|
|
for (auto UI : MUI->getUses()) {
|
|
SILInstruction *User = UI->getUser();
|
|
|
|
// Stores to self are initializations store or the rebind of self as
|
|
// part of the super.init call. Ignore both of these.
|
|
if (isa<StoreInst>(User) && UI->getOperandNumber() == 1)
|
|
continue;
|
|
|
|
// For class initializers, the assign into the self box may be
|
|
// captured as SelfInit or SuperInit elsewhere.
|
|
if (TheMemory.isClassInitSelf() && isa<AssignInst>(User) &&
|
|
UI->getOperandNumber() == 1) {
|
|
// If the source of the assignment is an application of a C
|
|
// function, there is no metatype argument, so treat the
|
|
// assignment to the self box as the initialization.
|
|
if (auto apply = dyn_cast<ApplyInst>(cast<AssignInst>(User)->getSrc())) {
|
|
if (auto fn = apply->getCalleeFunction()) {
|
|
if (fn->getRepresentation() ==
|
|
SILFunctionTypeRepresentation::CFunctionPointer) {
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stores *to* the allocation are writes. If the value being stored is a
|
|
// call to self.init()... then we have a self.init call.
|
|
if (auto *AI = dyn_cast<AssignInst>(User)) {
|
|
if (auto *AssignSource = dyn_cast<SILInstruction>(AI->getOperand(0)))
|
|
if (isSelfInitUse(AssignSource)) {
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
|
|
continue;
|
|
}
|
|
if (auto *AssignSource = dyn_cast<SILArgument>(AI->getOperand(0)))
|
|
if (AssignSource->getParent() == AI->getParent())
|
|
if (isSelfInitUse(AssignSource)) {
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (auto *CAI = dyn_cast<CopyAddrInst>(User)) {
|
|
if (isSelfInitUse(CAI)) {
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Loads of the box produce self, so collect uses from them.
|
|
if (auto *LI = dyn_cast<LoadInst>(User)) {
|
|
collectDelegatingClassInitSelfLoadUses(MUI, LI);
|
|
continue;
|
|
}
|
|
|
|
// destroyaddr on the box is load+release, which is treated as a release.
|
|
if (isa<DestroyAddrInst>(User)) {
|
|
Releases.push_back(User);
|
|
continue;
|
|
}
|
|
|
|
// We can safely handle anything else as an escape. They should all happen
|
|
// after self.init is invoked.
|
|
Uses.push_back(DIMemoryUse(User, DIUseKind::Escape, 0, 1));
|
|
}
|
|
|
|
// The MUI must be used on a project_box or alloc_stack instruction. Chase
|
|
// down the box value to see if there are any releases.
|
|
if (isa<AllocStackInst>(MUI->getOperand()))
|
|
return;
|
|
|
|
auto *PBI = cast<ProjectBoxInst>(MUI->getOperand());
|
|
auto *ABI = cast<AllocBoxInst>(PBI->getOperand());
|
|
|
|
for (auto UI : ABI->getUses()) {
|
|
SILInstruction *User = UI->getUser();
|
|
if (isa<StrongReleaseInst>(User))
|
|
Releases.push_back(User);
|
|
}
|
|
}
|
|
|
|
|
|
void ElementUseCollector::collectDelegatingValueTypeInitSelfUses() {
|
|
// When we're analyzing a delegating constructor, we aren't field sensitive at
|
|
// all. Just treat all members of self as uses of the single
|
|
// non-field-sensitive value.
|
|
assert(TheMemory.NumElements == 1 && "delegating inits only have 1 bit");
|
|
|
|
auto *MUI = cast<MarkUninitializedInst>(TheMemory.MemoryInst);
|
|
|
|
for (auto UI : MUI->getUses()) {
|
|
auto *User = UI->getUser();
|
|
|
|
// destroy_addr is a release of the entire value. This can be an early
|
|
// release for a conditional initializer.
|
|
if (isa<DestroyAddrInst>(User)) {
|
|
Releases.push_back(User);
|
|
continue;
|
|
}
|
|
|
|
// We only track two kinds of uses for delegating initializers:
|
|
// calls to self.init, and "other", which we choose to model as escapes.
|
|
// This intentionally ignores all stores, which (if they got emitted as
|
|
// copyaddr or assigns) will eventually get rewritten as assignments
|
|
// (not initializations), which is the right thing to do.
|
|
DIUseKind Kind = DIUseKind::Escape;
|
|
|
|
// Stores *to* the allocation are writes. If the value being stored is a
|
|
// call to self.init()... then we have a self.init call.
|
|
if (auto *AI = dyn_cast<AssignInst>(User)) {
|
|
if (auto *AssignSource = dyn_cast<SILInstruction>(AI->getOperand(0)))
|
|
if (isSelfInitUse(AssignSource))
|
|
Kind = DIUseKind::SelfInit;
|
|
if (auto *AssignSource = dyn_cast<SILArgument>(AI->getOperand(0)))
|
|
if (AssignSource->getParent() == AI->getParent())
|
|
if (isSelfInitUse(AssignSource))
|
|
Kind = DIUseKind::SelfInit;
|
|
}
|
|
|
|
if (auto *CAI = dyn_cast<CopyAddrInst>(User)) {
|
|
if (isSelfInitUse(CAI))
|
|
Kind = DIUseKind::SelfInit;
|
|
}
|
|
|
|
|
|
// We can safely handle anything else as an escape. They should all happen
|
|
// after self.init is invoked.
|
|
Uses.push_back(DIMemoryUse(User, Kind, 0, 1));
|
|
}
|
|
}
|
|
|
|
/// collectDIElementUsesFrom - Analyze all uses of the specified allocation
|
|
/// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them
|
|
/// and storing the information found into the Uses and Releases lists.
|
|
void swift::collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo,
|
|
SmallVectorImpl<DIMemoryUse> &Uses,
|
|
SmallVectorImpl<TermInst*> &FailableInits,
|
|
SmallVectorImpl<SILInstruction*> &Releases,
|
|
bool isDIFinished,
|
|
bool TreatAddressToPointerAsInout) {
|
|
ElementUseCollector(MemoryInfo, Uses, FailableInits, Releases, isDIFinished,
|
|
TreatAddressToPointerAsInout)
|
|
.collectFrom();
|
|
}
|