blob: 21a3ca467e497824e023fa2c207a08c8207fef06 [file] [log] [blame]
//===--- ArgumentExplosionTransform.cpp -----------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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 "fso-argument-explosion-transform"
#include "FunctionSignatureOpts.h"
#include "llvm/Support/CommandLine.h"
using namespace swift;
static llvm::cl::opt<bool> FSODisableArgExplosion(
"sil-fso-disable-arg-explosion",
llvm::cl::desc("Do not perform argument explosion during FSO. Intended "
"only for testing purposes"));
//===----------------------------------------------------------------------===//
// Utility
//===----------------------------------------------------------------------===//
/// Return true if it's both legal and a good idea to explode this argument.
static bool shouldExplode(ArgumentDescriptor &argDesc,
ConsumedArgToEpilogueReleaseMatcher &ERM) {
// We cannot optimize the argument.
if (!argDesc.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 (argDesc.ProjTree.isSingleton())
return false;
auto *arg = argDesc.Arg;
if (!shouldExpand(arg->getModule(), arg->getType().getObjectType())) {
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 (arg->hasConvention(SILArgumentConvention::Direct_Owned) &&
ERM.hasSomeReleasesForArgument(arg))
return true;
unsigned explosionSize = argDesc.ProjTree.getLiveLeafCount();
return explosionSize >= 1 && explosionSize <= 3;
}
//===----------------------------------------------------------------------===//
// Implementation
//===----------------------------------------------------------------------===//
bool FunctionSignatureTransform::ArgumentExplosionAnalyzeParameters() {
// If we are not supposed to perform argument explosion, bail.
if (FSODisableArgExplosion)
return false;
SILFunction *F = TransformDescriptor.OriginalFunction;
// Did we decide we should optimize any parameter?
bool SignatureOptimize = false;
auto Args = F->begin()->getFunctionArguments();
ConsumedArgToEpilogueReleaseMatcher ArgToReturnReleaseMap(
RCIA->get(F), F, {SILArgumentConvention::Direct_Owned});
// Analyze the argument information.
for (unsigned i : indices(Args)) {
ArgumentDescriptor &A = TransformDescriptor.ArgumentDescList[i];
// If the argument is dead, there is no point in trying to explode it. The
// dead argument pass will get it.
if (A.IsEntirelyDead) {
continue;
}
// Do not optimize argument.
if (!A.canOptimizeLiveArg()) {
continue;
}
// Explosion of generic parameters is not supported yet.
if (A.Arg->getType().hasArchetype())
continue;
A.ProjTree.computeUsesAndLiveness(A.Arg);
A.Explode = shouldExplode(A, ArgToReturnReleaseMap);
// Modified self argument.
if (A.Explode && Args[i]->isSelf()) {
TransformDescriptor.shouldModifySelfArgument = true;
}
SignatureOptimize |= A.Explode;
}
return SignatureOptimize;
}
void FunctionSignatureTransform::ArgumentExplosionFinalizeOptimizedFunction() {
SILFunction *NewF = TransformDescriptor.OptimizedFunction.get();
SILBasicBlock *BB = &*NewF->begin();
SILBuilder Builder(BB->begin());
Builder.setCurrentDebugScope(BB->getParent()->getDebugScope());
unsigned TotalArgIndex = 0;
for (ArgumentDescriptor &AD : TransformDescriptor.ArgumentDescList) {
// If this argument descriptor was dead and we removed it, just skip it. Do
// not increment the argument index.
if (AD.WasErased) {
continue;
}
// Simply continue if do not explode.
if (!AD.Explode) {
TransformDescriptor.AIM[TotalArgIndex] = AD.Index;
++TotalArgIndex;
continue;
}
assert(!AD.IsEntirelyDead &&
"Should never see completely dead values here");
// OK, we need to explode this argument.
unsigned ArgOffset = ++TotalArgIndex;
unsigned OldArgIndex = ArgOffset - 1;
llvm::SmallVector<SILValue, 8> LeafValues;
// We do this in the same order as leaf types since ProjTree expects that
// the order of leaf values matches the order of leaf types.
llvm::SmallVector<const ProjectionTreeNode *, 8> LeafNodes;
AD.ProjTree.getLiveLeafNodes(LeafNodes);
for (auto *Node : LeafNodes) {
auto OwnershipKind = *AD.getTransformedOwnershipKind(Node->getType());
LeafValues.push_back(
BB->insertFunctionArgument(ArgOffset, Node->getType(), OwnershipKind,
BB->getArgument(OldArgIndex)->getDecl()));
TransformDescriptor.AIM[TotalArgIndex - 1] = AD.Index;
++ArgOffset;
++TotalArgIndex;
}
// Then go through the projection tree constructing aggregates and replacing
// uses.
AD.ProjTree.replaceValueUsesWithLeafUses(
Builder, BB->getParent()->getLocation(), LeafValues);
// We ignored debugvalue uses when we constructed the new arguments, in
// order to preserve as much information as possible, we construct a new
// value for OrigArg from the leaf values and use that in place of the
// OrigArg.
SILValue NewOrigArgValue = AD.ProjTree.computeExplodedArgumentValue(
Builder, BB->getParent()->getLocation(), LeafValues);
// Replace all uses of the original arg with the new value.
SILArgument *OrigArg = BB->getArgument(OldArgIndex);
OrigArg->replaceAllUsesWith(NewOrigArgValue);
// Now erase the old argument since it does not have any uses. We also
// decrement ArgOffset since we have one less argument now.
BB->eraseArgument(OldArgIndex);
--TotalArgIndex;
}
}