mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
For a long time, we have: 1. Created methods on SILArgument that only work on either function arguments or block arguments. 2. Created code paths in the compiler that only allow for "function" SILArguments or "block" SILArguments. This commit refactors SILArgument into two subclasses, SILPHIArgument and SILFunctionArgument, separates the function and block APIs onto the subclasses (leaving the common APIs on SILArgument). It also goes through and changes all places in the compiler that conditionalize on one of the forms of SILArgument to just use the relevant subclass. This is made easier by the relevant APIs not being on SILArgument anymore. If you take a quick look through you will see that the API now expresses a lot more of its intention. The reason why I am performing this refactoring now is that SILFunctionArguments have a ValueOwnershipKind defined by the given function's signature. On the other hand, SILBlockArguments have a stored ValueOwnershipKind. Rather than store ValueOwnershipKind in both instances and in the function case have a dead variable, I decided to just bite the bullet and fix this. rdar://29671437
176 lines
6.3 KiB
C++
176 lines
6.3 KiB
C++
//===--- FunctionSignatureOptUtils.h ----------------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 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/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.
|
|
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()) {}
|
|
|
|
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 {
|
|
return Arg->getType().isObject();
|
|
}
|
|
|
|
/// 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;
|
|
|
|
// 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;
|
|
}
|
|
};
|
|
|
|
/// 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);
|
|
|
|
/// Return true if this argument is used in a non-trivial way.
|
|
bool hasNonTrivialNonDebugUse(SILArgument *Arg);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|