mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
405 lines
14 KiB
C++
405 lines
14 KiB
C++
//===--- FunctionSignatureOptUtils.cpp ------------------------------------===//
|
|
//
|
|
// 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 http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SIL/SILBasicBlock.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SIL/SILValue.h"
|
|
#include "swift/SIL/DebugUtils.h"
|
|
#include "swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h"
|
|
#include "swift/SILOptimizer/Utils/Local.h"
|
|
#include "swift/SIL/Mangle.h"
|
|
|
|
using namespace swift;
|
|
|
|
static bool isSpecializableRepresentation(SILFunctionTypeRepresentation Rep) {
|
|
switch (Rep) {
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
return true;
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::Block:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Returns true if F is a function which the pass know show to specialize
|
|
/// function signatures for.
|
|
bool swift::canSpecializeFunction(SILFunction *F) {
|
|
// Do not specialize the signature of SILFunctions that are external
|
|
// declarations since there is no body to optimize.
|
|
if (F->isExternalDeclaration())
|
|
return false;
|
|
|
|
// Do not specialize functions that are available externally. If an external
|
|
// function was able to be specialized, it would have been specialized in its
|
|
// own module. We will inline the original function as a thunk. The thunk will
|
|
// call the specialized function.
|
|
if (F->isAvailableExternally())
|
|
return false;
|
|
|
|
// Do not specialize the signature of always inline functions. We
|
|
// will just inline them and specialize each one of the individual
|
|
// functions that these sorts of functions are inlined into.
|
|
if (F->getInlineStrategy() == Inline_t::AlwaysInline)
|
|
return false;
|
|
|
|
// For now ignore generic functions to keep things simple...
|
|
if (F->getLoweredFunctionType()->isPolymorphic())
|
|
return false;
|
|
|
|
// Make sure F has a linkage that we can optimize.
|
|
if (!isSpecializableRepresentation(F->getRepresentation()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void swift::
|
|
addReleasesForConvertedOwnedParameter(SILBuilder &Builder,
|
|
SILLocation Loc,
|
|
ArrayRef<SILArgument*> Parameters,
|
|
ArrayRef<ArgumentDescriptor> &ArgDescs) {
|
|
// If we have any arguments that were consumed but are now guaranteed,
|
|
// insert a release_value.
|
|
for (auto &ArgDesc : ArgDescs) {
|
|
if (ArgDesc.CalleeRelease.empty())
|
|
continue;
|
|
Builder.createReleaseValue(Loc, Parameters[ArgDesc.Index],
|
|
Atomicity::Atomic);
|
|
}
|
|
}
|
|
|
|
void swift::
|
|
addReleasesForConvertedOwnedParameter(SILBuilder &Builder,
|
|
SILLocation Loc,
|
|
OperandValueArrayRef Parameters,
|
|
ArrayRef<ArgumentDescriptor> &ArgDescs) {
|
|
// If we have any arguments that were consumed but are now guaranteed,
|
|
// insert a release_value.
|
|
for (auto &ArgDesc : ArgDescs) {
|
|
// The argument is dead. Make sure we have a release to balance out
|
|
// the retain for creating the @owned parameter.
|
|
if (ArgDesc.IsEntirelyDead &&
|
|
ArgDesc.Arg->getKnownParameterInfo().getConvention() ==
|
|
ParameterConvention::Direct_Owned) {
|
|
Builder.createReleaseValue(Loc, Parameters[ArgDesc.Index],
|
|
Atomicity::Atomic);
|
|
continue;
|
|
}
|
|
if (ArgDesc.CalleeRelease.empty())
|
|
continue;
|
|
Builder.createReleaseValue(Loc, Parameters[ArgDesc.Index],
|
|
Atomicity::Atomic);
|
|
}
|
|
}
|
|
|
|
void swift::
|
|
addRetainsForConvertedDirectResults(SILBuilder &Builder,
|
|
SILLocation Loc,
|
|
SILValue ReturnValue,
|
|
SILInstruction *AI,
|
|
ArrayRef<ResultDescriptor> DirectResults) {
|
|
for (auto I : indices(DirectResults)) {
|
|
auto &RV = DirectResults[I];
|
|
if (RV.CalleeRetain.empty()) continue;
|
|
|
|
bool IsSelfRecursionEpilogueRetain = false;
|
|
for (auto &X : RV.CalleeRetain) {
|
|
IsSelfRecursionEpilogueRetain |= (AI == X);
|
|
}
|
|
|
|
// We do not create a retain if this ApplyInst is a self-recursion.
|
|
if (IsSelfRecursionEpilogueRetain)
|
|
continue;
|
|
|
|
// Extract the return value if necessary.
|
|
SILValue SpecificResultValue = ReturnValue;
|
|
if (DirectResults.size() != 1)
|
|
SpecificResultValue = Builder.createTupleExtract(Loc, ReturnValue, I);
|
|
|
|
Builder.createRetainValue(Loc, SpecificResultValue, Atomicity::Atomic);
|
|
}
|
|
}
|
|
|
|
using namespace swift;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Utility
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Returns true if I is a release instruction.
|
|
static bool isRelease(SILInstruction *I) {
|
|
switch (I->getKind()) {
|
|
case ValueKind::StrongReleaseInst:
|
|
case ValueKind::ReleaseValueInst:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Returns true if LHS and RHS contain identical set of releases.
|
|
static bool hasIdenticalReleases(ReleaseList LHS, ReleaseList RHS) {
|
|
llvm::DenseSet<SILInstruction *> Releases;
|
|
if (LHS.size() != RHS.size())
|
|
return false;
|
|
for (auto &X : LHS)
|
|
Releases.insert(X);
|
|
for (auto &X : RHS)
|
|
if (Releases.find(X) == Releases.end())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static ReleaseSet
|
|
collectEpilogueReleases(ConsumedArgToEpilogueReleaseMatcher &Return,
|
|
ConsumedArgToEpilogueReleaseMatcher &Throw,
|
|
SILArgument *Arg) {
|
|
ReleaseSet EpilogueReleases;
|
|
// Handle return block.
|
|
auto ReturnReleases = Return.getReleasesForArgument(Arg);
|
|
if (ReturnReleases.empty())
|
|
return EpilogueReleases;
|
|
for (auto &X : Return.getReleasesForArgument(Arg))
|
|
EpilogueReleases.insert(X);
|
|
|
|
// Handle throw block.
|
|
if (!Throw.hasBlock())
|
|
return EpilogueReleases;
|
|
auto ThrowReleases = Throw.getReleasesForArgument(Arg);
|
|
if (ThrowReleases.empty()) {
|
|
EpilogueReleases.clear();
|
|
return EpilogueReleases;
|
|
}
|
|
for (auto &X : ThrowReleases)
|
|
EpilogueReleases.insert(X);
|
|
|
|
return EpilogueReleases;
|
|
}
|
|
|
|
/// Returns .Some(I) if I is a release that is the only non-debug instruction
|
|
/// with side-effects in the use-def graph originating from Arg. Returns
|
|
/// .Some(nullptr), if all uses from the arg were either debug insts or do not
|
|
/// have side-effects. Returns .None if there were any non-release instructions
|
|
/// with side-effects in the use-def graph from Arg or if there were multiple
|
|
/// release instructions with side-effects in the use-def graph from Arg.
|
|
static llvm::Optional<ReleaseList>
|
|
getNonTrivialNonDebugReleaseUse(SILArgument *Arg) {
|
|
llvm::SmallVector<SILInstruction *, 8> Worklist;
|
|
llvm::SmallPtrSet<SILInstruction *, 8> SeenInsts;
|
|
ReleaseList Result;
|
|
|
|
for (Operand *I : getNonDebugUses(SILValue(Arg)))
|
|
Worklist.push_back(I->getUser());
|
|
|
|
while (!Worklist.empty()) {
|
|
SILInstruction *U = Worklist.pop_back_val();
|
|
if (!SeenInsts.insert(U).second)
|
|
continue;
|
|
|
|
// If U is a terminator inst, return false.
|
|
if (isa<TermInst>(U))
|
|
return None;
|
|
|
|
// If U has side effects...
|
|
if (U->mayHaveSideEffects()) {
|
|
// And is not a release_value, return None.
|
|
if (!isRelease(U))
|
|
return None;
|
|
|
|
// Otherwise, set result to that value.
|
|
Result.push_back(U);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise add all non-debug uses of I to the worklist.
|
|
for (Operand *I : getNonDebugUses(U))
|
|
Worklist.push_back(I->getUser());
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
bool FunctionSignatureInfo::analyzeParameters() {
|
|
// For now ignore functions with indirect results.
|
|
if (F->getLoweredFunctionType()->hasIndirectResults())
|
|
return false;
|
|
|
|
ArrayRef<SILArgument *> Args = F->begin()->getBBArgs();
|
|
|
|
// A map from consumed SILArguments to the release associated with an
|
|
// argument.
|
|
ConsumedArgToEpilogueReleaseMatcher ArgToReturnReleaseMap(RCFI, F);
|
|
ConsumedArgToEpilogueReleaseMatcher ArgToThrowReleaseMap(
|
|
RCFI, F, ConsumedArgToEpilogueReleaseMatcher::ExitKind::Throw);
|
|
|
|
// Did we decide we should optimize any parameter?
|
|
bool SignatureOptimize = false;
|
|
|
|
// Analyze the argument information.
|
|
for (unsigned i = 0, e = Args.size(); i != e; ++i) {
|
|
// Find all the epilogue releases for this argument.
|
|
auto EpilogueReleases = collectEpilogueReleases(ArgToReturnReleaseMap,
|
|
ArgToThrowReleaseMap,
|
|
Args[i]);
|
|
ArgumentDescriptor A(Allocator, Args[i], EpilogueReleases);
|
|
bool HaveOptimizedArg = false;
|
|
|
|
// Whether we will explode the argument or not.
|
|
A.Explode = A.shouldExplode();
|
|
|
|
bool isABIRequired = isArgumentABIRequired(Args[i]);
|
|
auto OnlyRelease = getNonTrivialNonDebugReleaseUse(Args[i]);
|
|
|
|
// If this argument is not ABI required and has no uses except for debug
|
|
// instructions, remove it.
|
|
if (!isABIRequired && OnlyRelease && OnlyRelease.getValue().empty()) {
|
|
A.IsEntirelyDead = true;
|
|
HaveOptimizedArg = true;
|
|
}
|
|
|
|
// See if we can find a ref count equivalent strong_release or release_value
|
|
// at the end of this function if our argument is an @owned parameter.
|
|
if (A.hasConvention(SILArgumentConvention::Direct_Owned)) {
|
|
auto Releases = ArgToReturnReleaseMap.getReleasesForArgument(A.Arg);
|
|
if (!Releases.empty()) {
|
|
|
|
// If the function has a throw block we must also find a matching
|
|
// release in the throw block.
|
|
auto ReleasesInThrow = ArgToThrowReleaseMap.getReleasesForArgument(A.Arg);
|
|
if (!ArgToThrowReleaseMap.hasBlock() || !ReleasesInThrow.empty()) {
|
|
|
|
// TODO: accept a second release in the throw block to let the
|
|
// argument be dead.
|
|
if (OnlyRelease && hasIdenticalReleases(OnlyRelease.getValue(), Releases)) {
|
|
A.IsEntirelyDead = true;
|
|
}
|
|
|
|
A.CalleeRelease = Releases;
|
|
A.CalleeReleaseInThrowBlock = ReleasesInThrow;
|
|
HaveOptimizedArg = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (A.Explode) {
|
|
HaveOptimizedArg = true;
|
|
}
|
|
|
|
if (HaveOptimizedArg) {
|
|
SignatureOptimize = true;
|
|
// Store that we have modified the self argument. We need to change the
|
|
// calling convention later.
|
|
if (Args[i]->isSelf())
|
|
ShouldModifySelfArgument = true;
|
|
}
|
|
|
|
// Add the argument to our list.
|
|
ArgDescList.push_back(std::move(A));
|
|
}
|
|
|
|
return SignatureOptimize;
|
|
}
|
|
|
|
bool FunctionSignatureInfo::analyzeResult() {
|
|
// For now ignore functions with indirect results.
|
|
if (F->getLoweredFunctionType()->hasIndirectResults())
|
|
return false;
|
|
|
|
// Did we decide we should optimize any parameter?
|
|
bool SignatureOptimize = false;
|
|
|
|
// Analyze return result information.
|
|
auto DirectResults = F->getLoweredFunctionType()->getDirectResults();
|
|
for (SILResultInfo DirectResult : DirectResults) {
|
|
ResultDescList.emplace_back(DirectResult);
|
|
}
|
|
// For now, only do anything if there's a single direct result.
|
|
if (DirectResults.size() == 1 &&
|
|
ResultDescList[0].hasConvention(ResultConvention::Owned)) {
|
|
auto &RI = ResultDescList[0];
|
|
// We have an @owned return value, find the epilogue retains now.
|
|
ConsumedResultToEpilogueRetainMatcher RVToReturnRetainMap(RCFI, AA, F);
|
|
auto Retains = RVToReturnRetainMap.getEpilogueRetains();
|
|
// We do not need to worry about the throw block, as the return value is only
|
|
// going to be used in the return block/normal block of the try_apply instruction.
|
|
if (!Retains.empty()) {
|
|
RI.CalleeRetain = Retains;
|
|
SignatureOptimize = true;
|
|
}
|
|
}
|
|
return SignatureOptimize;
|
|
}
|
|
|
|
/// This function goes through the arguments of F and sees if we have anything
|
|
/// to optimize in which case it returns true. If we have nothing to optimize,
|
|
/// it returns false.
|
|
void FunctionSignatureInfo::analyze() {
|
|
// Compute the signature optimization.
|
|
bool OptimizedParams = analyzeParameters();
|
|
bool OptimizedResult = analyzeResult();
|
|
ShouldOptimize = OptimizedParams || OptimizedResult;
|
|
// We set this function to highly profitable if we have a O2G on one of its
|
|
// parameters or results.
|
|
for (auto &X : ArgDescList) {
|
|
HighlyProfitable |= !X.CalleeRelease.empty();
|
|
}
|
|
for (auto &X : ResultDescList) {
|
|
HighlyProfitable |= !X.CalleeRetain.empty();
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Mangling
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
std::string FunctionSignatureInfo::getOptimizedName() const {
|
|
Mangle::Mangler M;
|
|
auto P = SpecializationPass::FunctionSignatureOpts;
|
|
FunctionSignatureSpecializationMangler FSSM(P, M, F);
|
|
|
|
// Handle arguments' changes.
|
|
for (unsigned i : indices(ArgDescList)) {
|
|
const ArgumentDescriptor &Arg = ArgDescList[i];
|
|
if (Arg.IsEntirelyDead) {
|
|
FSSM.setArgumentDead(i);
|
|
}
|
|
|
|
// If we have an @owned argument and found a callee release for it,
|
|
// convert the argument to guaranteed.
|
|
if (!Arg.CalleeRelease.empty()) {
|
|
FSSM.setArgumentOwnedToGuaranteed(i);
|
|
}
|
|
|
|
// If this argument is not dead and we can explode it, add 's' to the
|
|
// mangling.
|
|
if (Arg.Explode && !Arg.IsEntirelyDead) {
|
|
FSSM.setArgumentSROA(i);
|
|
}
|
|
}
|
|
|
|
// Handle return value's change.
|
|
// FIXME: handle multiple direct results here
|
|
if (ResultDescList.size() == 1 &&
|
|
!ResultDescList[0].CalleeRetain.empty())
|
|
FSSM.setReturnValueOwnedToUnowned();
|
|
|
|
FSSM.mangle();
|
|
|
|
return M.finalize();
|
|
}
|