mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
283 lines
11 KiB
C++
283 lines
11 KiB
C++
//===--- GenericCloner.cpp - Specializes generic functions ---------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SILOptimizer/Utils/GenericCloner.h"
|
|
|
|
#include "swift/AST/Type.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/SIL/OwnershipUtils.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#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/ScopedAddressUtils.h"
|
|
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
|
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// Create a new empty function with the correct arguments and a unique name.
|
|
SILFunction *GenericCloner::createDeclaration(
|
|
SILOptFunctionBuilder &FunctionBuilder, SILFunction *Orig,
|
|
const ReabstractionInfo &ReInfo, StringRef NewName) {
|
|
assert((Orig->isTransparent() || Orig->isBare() || Orig->getLocation())
|
|
&& "SILFunction missing location");
|
|
assert((Orig->isTransparent() || Orig->isBare() || Orig->getDebugScope())
|
|
&& "SILFunction missing DebugScope");
|
|
assert(!Orig->isGlobalInit() && "Global initializer cannot be cloned");
|
|
|
|
// Create a new empty function.
|
|
SILFunction *NewF = FunctionBuilder.createFunction(
|
|
getSpecializedLinkage(Orig, Orig->getLinkage()), NewName,
|
|
ReInfo.getSpecializedType(), ReInfo.getSpecializedGenericEnvironment(),
|
|
Orig->getLocation(), Orig->isBare(), Orig->isTransparent(),
|
|
ReInfo.getSerializedKind(), IsNotDynamic, IsNotDistributed,
|
|
IsNotRuntimeAccessible, Orig->getEntryCount(), Orig->isThunk(),
|
|
Orig->getClassSubclassScope(), Orig->getInlineStrategy(),
|
|
Orig->getEffectsKind(), Orig, Orig->getDebugScope());
|
|
for (auto &Attr : Orig->getSemanticsAttrs()) {
|
|
NewF->addSemanticsAttr(Attr);
|
|
}
|
|
NewF->setOptimizationMode(Orig->getOptimizationMode());
|
|
if (!Orig->hasOwnership()) {
|
|
NewF->setOwnershipEliminated();
|
|
}
|
|
NewF->copyEffects(Orig);
|
|
return NewF;
|
|
}
|
|
|
|
void GenericCloner::populateCloned() {
|
|
assert(AllocStacks.empty() && "Stale cloner state.");
|
|
assert(!ReturnValueAddr && "Stale cloner state.");
|
|
assert(!ErrorValueAddr && "Stale cloner state.");
|
|
|
|
SILFunction *Cloned = getCloned();
|
|
// Create arguments for the entry block.
|
|
SILBasicBlock *OrigEntryBB = &*Original.begin();
|
|
SILBasicBlock *ClonedEntryBB = Cloned->createBasicBlock();
|
|
getBuilder().setInsertionPoint(ClonedEntryBB);
|
|
|
|
RemappedScopeCache.insert({Original.getDebugScope(), Cloned->getDebugScope()});
|
|
|
|
// Create the entry basic block with the function arguments.
|
|
auto origConv = Original.getConventions();
|
|
unsigned ArgIdx = 0;
|
|
SmallVector<SILValue, 4> entryArgs;
|
|
entryArgs.reserve(OrigEntryBB->getArguments().size());
|
|
for (auto &OrigArg : OrigEntryBB->getArguments()) {
|
|
RegularLocation Loc = OrigArg->getDecl() ?
|
|
RegularLocation((Decl *)OrigArg->getDecl()) :
|
|
RegularLocation::getAutoGeneratedLocation();
|
|
AllocStackInst *ASI = nullptr;
|
|
SILType mappedType = remapType(OrigArg->getType());
|
|
|
|
auto createAllocStack = [&]() {
|
|
// We need an alloc_stack as a replacement for the indirect parameter.
|
|
if (mappedType.isAddress()) {
|
|
mappedType = mappedType.getObjectType();
|
|
}
|
|
auto AllocStackLoc = RegularLocation::getAutoGeneratedLocation();
|
|
ASI = getBuilder().createAllocStack(AllocStackLoc, mappedType);
|
|
AllocStacks.push_back(ASI);
|
|
};
|
|
auto handleConversion = [&]() {
|
|
if (!origConv.useLoweredAddresses())
|
|
return false;
|
|
|
|
if (ArgIdx < origConv.getSILArgIndexOfFirstParam()) {
|
|
if (ArgIdx < origConv.getNumIndirectSILResults()) {
|
|
// Handle result arguments.
|
|
unsigned formalIdx =
|
|
origConv.getIndirectFormalResultIndexForSILArg(ArgIdx);
|
|
if (ReInfo.isFormalResultConverted(formalIdx)) {
|
|
// This result is converted from indirect to direct. The return inst
|
|
// needs to load the value from the alloc_stack. See below.
|
|
createAllocStack();
|
|
assert(!ReturnValueAddr);
|
|
ReturnValueAddr = ASI;
|
|
entryArgs.push_back(ASI);
|
|
return true;
|
|
}
|
|
} else {
|
|
assert(origConv.getNumIndirectSILErrorResults() == 1 &&
|
|
"only a single indirect error result is supported");
|
|
assert(ArgIdx == origConv.getNumIndirectSILResults());
|
|
|
|
if (ReInfo.isErrorResultConverted()) {
|
|
// This error result is converted from indirect to direct. The throw
|
|
// instruction needs to load the value from the alloc_stack. See below.
|
|
createAllocStack();
|
|
assert(!ErrorValueAddr);
|
|
ErrorValueAddr = ASI;
|
|
entryArgs.push_back(ASI);
|
|
return true;
|
|
}
|
|
}
|
|
} else if (ReInfo.isDroppedArgument(ArgIdx)) {
|
|
if (OrigArg->getType().isAddress()) {
|
|
// Create an uninitialized alloc_stack for unused indirect arguments.
|
|
// This will be removed by other optimizations.
|
|
createAllocStack();
|
|
entryArgs.push_back(ASI);
|
|
} else {
|
|
// Dropped direct (= non-address) arguments are metatype arguments.
|
|
// Replace the metatype argument with an `metatype` instruction in the
|
|
// entry block.
|
|
auto *mtInst = getBuilder().createMetatype(Loc, mappedType);
|
|
entryArgs.push_back(mtInst);
|
|
}
|
|
return true;
|
|
} else {
|
|
// Handle arguments for formal parameters.
|
|
unsigned paramIdx = ArgIdx - origConv.getSILArgIndexOfFirstParam();
|
|
if (ReInfo.isParamConverted(paramIdx)) {
|
|
assert(mappedType.isAddress());
|
|
mappedType = mappedType.getObjectType();
|
|
auto *NewArg = ClonedEntryBB->createFunctionArgument(
|
|
mappedType, OrigArg->getDecl());
|
|
NewArg->copyFlags(cast<SILFunctionArgument>(OrigArg));
|
|
|
|
// Store the new direct parameter to an alloc_stack.
|
|
createAllocStack();
|
|
SILValue addr;
|
|
if (NewArg->getArgumentConvention()
|
|
.isGuaranteedConventionInCallee() &&
|
|
NewArg->getFunction()->hasOwnership()) {
|
|
auto *sbi = getBuilder().createStoreBorrow(Loc, NewArg, ASI);
|
|
StoreBorrowsToCleanup.push_back(sbi);
|
|
addr = sbi;
|
|
} else {
|
|
getBuilder().emitStoreValueOperation(Loc, NewArg, ASI,
|
|
StoreOwnershipQualifier::Init);
|
|
addr = ASI;
|
|
}
|
|
|
|
entryArgs.push_back(addr);
|
|
return true;
|
|
}
|
|
}
|
|
return false; // No conversion.
|
|
};
|
|
if (!handleConversion()) {
|
|
auto *NewArg =
|
|
ClonedEntryBB->createFunctionArgument(mappedType, OrigArg->getDecl());
|
|
NewArg->copyFlags(cast<SILFunctionArgument>(OrigArg));
|
|
entryArgs.push_back(NewArg);
|
|
}
|
|
++ArgIdx;
|
|
}
|
|
|
|
// Visit original BBs in depth-first preorder, starting with the
|
|
// entry block, cloning all instructions and terminators.
|
|
cloneFunctionBody(&Original, ClonedEntryBB, entryArgs);
|
|
}
|
|
|
|
void GenericCloner::visitTerminator(SILBasicBlock *BB) {
|
|
TermInst *OrigTermInst = BB->getTerminator();
|
|
if (auto *RI = dyn_cast<ReturnInst>(OrigTermInst)) {
|
|
SILValue ReturnValue;
|
|
if (ReturnValueAddr) {
|
|
// The result is converted from indirect to direct. We have to load the
|
|
// returned value from the alloc_stack.
|
|
ReturnValue = getBuilder().emitLoadValueOperation(
|
|
ReturnValueAddr->getLoc(), ReturnValueAddr,
|
|
LoadOwnershipQualifier::Take);
|
|
}
|
|
for (AllocStackInst *ASI : reverse(AllocStacks)) {
|
|
getBuilder().createDeallocStack(ASI->getLoc(), ASI);
|
|
}
|
|
if (ReturnValue) {
|
|
getBuilder().createReturn(RI->getLoc(), ReturnValue);
|
|
return;
|
|
}
|
|
} else if (isa<ThrowAddrInst>(OrigTermInst) && ErrorValueAddr) {
|
|
// The result is converted from indirect to direct. We have to load the
|
|
// returned value from the alloc_stack.
|
|
SILValue errorValue = getBuilder().emitLoadValueOperation(
|
|
ErrorValueAddr->getLoc(), ErrorValueAddr,
|
|
LoadOwnershipQualifier::Take);
|
|
for (AllocStackInst *ASI : reverse(AllocStacks)) {
|
|
getBuilder().createDeallocStack(ASI->getLoc(), ASI);
|
|
}
|
|
getBuilder().createThrow(OrigTermInst->getLoc(), errorValue);
|
|
return;
|
|
} else if (OrigTermInst->isFunctionExiting()) {
|
|
for (AllocStackInst *ASI : reverse(AllocStacks)) {
|
|
getBuilder().createDeallocStack(ASI->getLoc(), ASI);
|
|
}
|
|
}
|
|
visit(OrigTermInst);
|
|
}
|
|
|
|
const SILDebugScope *GenericCloner::remapScope(const SILDebugScope *DS) {
|
|
if (!DS)
|
|
return nullptr;
|
|
auto it = RemappedScopeCache.find(DS);
|
|
if (it != RemappedScopeCache.end())
|
|
return it->second;
|
|
|
|
auto &M = getBuilder().getModule();
|
|
auto *ParentFunction = DS->Parent.dyn_cast<SILFunction *>();
|
|
if (ParentFunction == &Original)
|
|
ParentFunction = getCloned();
|
|
else if (ParentFunction)
|
|
ParentFunction = remapParentFunction(
|
|
FuncBuilder, M, ParentFunction, SubsMap,
|
|
Original.getLoweredFunctionType()->getInvocationGenericSignature());
|
|
|
|
auto *ParentScope = DS->Parent.dyn_cast<const SILDebugScope *>();
|
|
auto *RemappedScope =
|
|
new (M) SILDebugScope(DS->Loc, ParentFunction, remapScope(ParentScope),
|
|
remapScope(DS->InlinedCallSite));
|
|
RemappedScopeCache.insert({DS, RemappedScope});
|
|
return RemappedScope;
|
|
}
|
|
|
|
void GenericCloner::postFixUp(SILFunction *f) {
|
|
for (auto *apply : noReturnApplies) {
|
|
auto applyBlock = apply->getParent();
|
|
applyBlock->split(std::next(SILBasicBlock::iterator(apply)));
|
|
getBuilder().setInsertionPoint(applyBlock);
|
|
getBuilder().createUnreachable(apply->getLoc());
|
|
}
|
|
|
|
SmallVector<SILBasicBlock *, 4> discoveredBlocks;
|
|
for (auto *sbi : StoreBorrowsToCleanup) {
|
|
ScopedAddressValue scopedAddress(sbi);
|
|
if (scopedAddress->getUses().empty()) {
|
|
scopedAddress.createScopeEnd(sbi->getNextInstruction()->getIterator(),
|
|
RegularLocation::getAutoGeneratedLocation());
|
|
continue;
|
|
}
|
|
discoveredBlocks.clear();
|
|
// FIXME: Call OSSA lifetime fixup on all values used within the unreachable
|
|
// code. This will recursively fixup nested scopes from the inside out so
|
|
// that transitive liveness is not required.
|
|
SSAPrunedLiveness storeBorrowLiveness(getCloned(), &discoveredBlocks);
|
|
AddressUseKind useKind =
|
|
scopedAddress.computeTransitiveLiveness(storeBorrowLiveness);
|
|
if (useKind == AddressUseKind::NonEscaping) {
|
|
scopedAddress.endScopeAtLivenessBoundary(&storeBorrowLiveness);
|
|
continue;
|
|
}
|
|
auto *alloc = cast<AllocStackInst>(sbi->getDest());
|
|
for (auto *dealloc : alloc->getUsersOfType<DeallocStackInst>()) {
|
|
scopedAddress.createScopeEnd(dealloc->getIterator(),
|
|
RegularLocation::getAutoGeneratedLocation());
|
|
}
|
|
}
|
|
}
|