mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
209 lines
7.4 KiB
C++
209 lines
7.4 KiB
C++
//===--- FunctionSignatureOptUtils.h ----------------------------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_FUNCTIONSIGOPTUTILS_H
|
|
#define SWIFT_SIL_FUNCTIONSIGOPTUTILS_H
|
|
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILValue.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
#include "swift/SIL/SILDebugScope.h"
|
|
#include "swift/SIL/Projection.h"
|
|
#include "swift/SILOptimizer/Analysis/Analysis.h"
|
|
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/CallerAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h"
|
|
#include "swift/SILOptimizer/Utils/Local.h"
|
|
#include "swift/SILOptimizer/PassManager/PassManager.h"
|
|
|
|
namespace swift {
|
|
|
|
/// A structure that maintains all of the information about a specific
|
|
/// SILArgument that we are tracking.
|
|
struct ArgumentDescriptor {
|
|
/// The argument that we are tracking original data for.
|
|
SILFunctionArgument *Arg;
|
|
|
|
/// Parameter Info.
|
|
Optional<SILParameterInfo> PInfo;
|
|
|
|
/// The original index of this argument.
|
|
unsigned Index;
|
|
|
|
/// The original decl of this Argument.
|
|
const ValueDecl *Decl;
|
|
|
|
/// Was this parameter originally dead?
|
|
bool IsEntirelyDead;
|
|
|
|
/// Should the argument be exploded ?
|
|
bool Explode;
|
|
|
|
/// This parameter is owned to guaranteed.
|
|
bool OwnedToGuaranteed;
|
|
|
|
/// Is this parameter an indirect result?
|
|
bool IsIndirectResult;
|
|
|
|
/// If non-null, this is the release in the return block of the callee, which
|
|
/// is associated with this parameter if it is @owned. If the parameter is not
|
|
/// @owned or we could not find such a release in the callee, this is null.
|
|
ReleaseList CalleeRelease;
|
|
|
|
/// The same as CalleeRelease, but the release in the throw block, if it is a
|
|
/// function which has a throw block.
|
|
ReleaseList CalleeReleaseInThrowBlock;
|
|
|
|
/// The projection tree of this arguments.
|
|
ProjectionTree ProjTree;
|
|
|
|
ArgumentDescriptor() = delete;
|
|
|
|
/// Initialize this argument descriptor with all information from A that we
|
|
/// use in our optimization.
|
|
///
|
|
/// *NOTE* We cache a lot of data from the argument and maintain a reference
|
|
/// to the original argument. The reason why we do this is to make sure we
|
|
/// have access to the original argument's state if we modify the argument
|
|
/// when optimizing.
|
|
ArgumentDescriptor(SILFunctionArgument *A)
|
|
: Arg(A),
|
|
PInfo(A->getKnownParameterInfo()),
|
|
Index(A->getIndex()),
|
|
Decl(A->getDecl()), IsEntirelyDead(false), Explode(false),
|
|
OwnedToGuaranteed(false), IsIndirectResult(A->isIndirectResult()),
|
|
CalleeRelease(), CalleeReleaseInThrowBlock(),
|
|
ProjTree(A->getModule(), A->getType()) {
|
|
if (!A->isIndirectResult()) {
|
|
PInfo = Arg->getKnownParameterInfo();
|
|
}
|
|
}
|
|
|
|
ArgumentDescriptor(const ArgumentDescriptor &) = delete;
|
|
ArgumentDescriptor(ArgumentDescriptor &&) = default;
|
|
ArgumentDescriptor &operator=(const ArgumentDescriptor &) = delete;
|
|
ArgumentDescriptor &operator=(ArgumentDescriptor &&) = default;
|
|
|
|
/// \returns true if this argument's convention is P.
|
|
bool hasConvention(SILArgumentConvention P) const {
|
|
return Arg->hasConvention(P);
|
|
}
|
|
|
|
bool canOptimizeLiveArg() const {
|
|
if (Arg->getType().isObject())
|
|
return true;
|
|
// @in arguments of generic types can be processed.
|
|
if (Arg->getType().getSwiftRValueType()->hasArchetype() &&
|
|
Arg->getType().isAddress() &&
|
|
(Arg->hasConvention(SILArgumentConvention::Indirect_In) ||
|
|
Arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// Return true if it's both legal and a good idea to explode this argument.
|
|
bool shouldExplode(ConsumedArgToEpilogueReleaseMatcher &ERM) const {
|
|
// We cannot optimize the argument.
|
|
if (!canOptimizeLiveArg())
|
|
return false;
|
|
|
|
// See if the projection tree consists of potentially multiple levels of
|
|
// structs containing one field. In such a case, there is no point in
|
|
// exploding the argument.
|
|
//
|
|
// Also, in case of a type can not be exploded, e.g an enum, we treat it
|
|
// as a singleton.
|
|
if (ProjTree.isSingleton())
|
|
return false;
|
|
|
|
auto Ty = Arg->getType().getObjectType();
|
|
if (!shouldExpand(Arg->getModule(), Ty)) {
|
|
return false;
|
|
}
|
|
|
|
// If this argument is @owned and we can not find all the releases for it
|
|
// try to explode it, maybe we can find some of the releases and O2G some
|
|
// of its components.
|
|
//
|
|
// This is a potentially a very profitable optimization. Ignore other
|
|
// heuristics.
|
|
if (hasConvention(SILArgumentConvention::Direct_Owned) &&
|
|
ERM.hasSomeReleasesForArgument(Arg))
|
|
return true;
|
|
|
|
size_t explosionSize = ProjTree.liveLeafCount();
|
|
return explosionSize >= 1 && explosionSize <= 3;
|
|
}
|
|
|
|
llvm::Optional<ValueOwnershipKind>
|
|
getTransformedOwnershipKind(SILType SubTy) {
|
|
if (IsEntirelyDead)
|
|
return None;
|
|
if (SubTy.isTrivial(Arg->getModule()))
|
|
return Optional<ValueOwnershipKind>(ValueOwnershipKind::Trivial);
|
|
if (OwnedToGuaranteed)
|
|
return Optional<ValueOwnershipKind>(ValueOwnershipKind::Guaranteed);
|
|
return Arg->getOwnershipKind();
|
|
}
|
|
};
|
|
|
|
/// A structure that maintains all of the information about a specific
|
|
/// direct result that we are tracking.
|
|
struct ResultDescriptor {
|
|
/// The original parameter info of this argument.
|
|
SILResultInfo ResultInfo;
|
|
|
|
/// If non-null, this is the release in the return block of the callee, which
|
|
/// is associated with this parameter if it is @owned. If the parameter is not
|
|
/// @owned or we could not find such a release in the callee, this is null.
|
|
llvm::SmallSetVector<SILInstruction *, 1> CalleeRetain;
|
|
|
|
/// This is owned to guaranteed.
|
|
bool OwnedToGuaranteed;
|
|
|
|
/// Initialize this argument descriptor with all information from A that we
|
|
/// use in our optimization.
|
|
///
|
|
/// *NOTE* We cache a lot of data from the argument and maintain a reference
|
|
/// to the original argument. The reason why we do this is to make sure we
|
|
/// have access to the original argument's state if we modify the argument
|
|
/// when optimizing.
|
|
ResultDescriptor() {}
|
|
ResultDescriptor(SILResultInfo RI)
|
|
: ResultInfo(RI), CalleeRetain(), OwnedToGuaranteed(false) {}
|
|
|
|
ResultDescriptor(const ResultDescriptor &) = delete;
|
|
ResultDescriptor(ResultDescriptor &&) = default;
|
|
ResultDescriptor &operator=(const ResultDescriptor &) = delete;
|
|
ResultDescriptor &operator=(ResultDescriptor &&) = default;
|
|
|
|
/// \returns true if this argument's ParameterConvention is P.
|
|
bool hasConvention(ResultConvention R) const {
|
|
return ResultInfo.getConvention() == R;
|
|
}
|
|
};
|
|
|
|
/// Returns true if F is a function which the pass know show to specialize
|
|
/// function signatures for.
|
|
bool canSpecializeFunction(SILFunction *F,
|
|
const CallerAnalysis::FunctionInfo *FuncInfo,
|
|
bool OptForPartialApply);
|
|
|
|
/// Return true if this argument is used in a non-trivial way.
|
|
bool hasNonTrivialNonDebugUse(SILArgument *Arg);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|