mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Previously it was testing for opened existentials specifically. We should really teach outlining to handle local archetypes properly. We'd have to build a generic signature for the lowered type, and that probably means also adding requirements that are relevant to value operations, but it would mean outlining would benefit all types, and it would let us avoid bundling in unnecessary information from the enclosing generic environment. A minor side-effect of this is that we no longer bind names to opened element type values. The names were things like \tau_1_0, which is not very useful, especially in LLVM IR where \tau is printed with two UTF-8 escapes.
460 lines
17 KiB
C++
460 lines
17 KiB
C++
//===--- Outlining.cpp - Outlining value operations -----------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements IR generation for outlined value operations in Swift.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Outlining.h"
|
|
|
|
#include "Explosion.h"
|
|
#include "GenOpaque.h"
|
|
#include "GenProto.h"
|
|
#include "IRGenFunction.h"
|
|
#include "IRGenMangler.h"
|
|
#include "IRGenModule.h"
|
|
#include "LoadableTypeInfo.h"
|
|
#include "LocalTypeDataKind.h"
|
|
#include "MetadataRequest.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/IRGenOptions.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
void OutliningMetadataCollector::collectTypeMetadataForLayout(SILType type) {
|
|
// If the type has no archetypes, we can emit it from scratch in the callee.
|
|
if (!type.hasArchetype()) {
|
|
return;
|
|
}
|
|
|
|
// Substitute opaque types if allowed.
|
|
type =
|
|
IGF.IGM.substOpaqueTypesWithUnderlyingTypes(type, CanGenericSignature());
|
|
|
|
auto formalType = type.getASTType();
|
|
auto &ti = IGF.IGM.getTypeInfoForLowered(formalType);
|
|
|
|
// We don't need the metadata for fixed size types or types that are not ABI
|
|
// accessible. Outlining will call the value witness of the enclosing type of
|
|
// non ABI accessible field/element types.
|
|
if (isa<FixedTypeInfo>(ti) || !ti.isABIAccessible()) {
|
|
return;
|
|
}
|
|
|
|
// If the type is a legal formal type, add it as a formal type.
|
|
// FIXME: does this force us to emit a more expensive metadata than we need
|
|
// to?
|
|
if (formalType->isLegalFormalType()) {
|
|
return collectFormalTypeMetadata(formalType);
|
|
}
|
|
|
|
auto key = LocalTypeDataKey(type.getASTType(),
|
|
LocalTypeDataKind::forRepresentationTypeMetadata());
|
|
if (Values.count(key)) return;
|
|
|
|
auto metadata = IGF.emitTypeMetadataRefForLayout(type);
|
|
Values.insert({key, metadata});
|
|
}
|
|
|
|
void OutliningMetadataCollector::collectFormalTypeMetadata(CanType type) {
|
|
// If the type has no archetypes, we can emit it from scratch in the callee.
|
|
assert(type->hasArchetype());
|
|
|
|
auto key = LocalTypeDataKey(type, LocalTypeDataKind::forFormalTypeMetadata());
|
|
if (Values.count(key)) return;
|
|
|
|
auto metadata = IGF.emitTypeMetadataRef(type);
|
|
Values.insert({key, metadata});
|
|
}
|
|
|
|
|
|
void OutliningMetadataCollector::addMetadataArguments(
|
|
SmallVectorImpl<llvm::Value*> &args) const {
|
|
for (auto &pair : Values) {
|
|
auto metadata = pair.second;
|
|
assert(metadata->getType() == IGF.IGM.TypeMetadataPtrTy);
|
|
args.push_back(metadata);
|
|
}
|
|
}
|
|
|
|
void OutliningMetadataCollector::addMetadataParameterTypes(
|
|
SmallVectorImpl<llvm::Type*> ¶mTys) const {
|
|
for (auto &pair : Values) {
|
|
auto *metadata = pair.second;
|
|
paramTys.push_back(metadata->getType());
|
|
}
|
|
}
|
|
|
|
void OutliningMetadataCollector::bindMetadataParameters(IRGenFunction &IGF,
|
|
Explosion ¶ms) const {
|
|
// Note that our parameter IGF intentionally shadows the IGF that this
|
|
// collector was built with.
|
|
for (auto &pair : Values) {
|
|
llvm::Value *arg = params.claimNext();
|
|
|
|
auto key = pair.first;
|
|
assert(key.Kind.isAnyTypeMetadata());
|
|
setTypeMetadataName(IGF.IGM, arg, key.Type);
|
|
IGF.setUnscopedLocalTypeData(key, MetadataResponse::forComplete(arg));
|
|
}
|
|
}
|
|
|
|
std::pair<CanType, CanGenericSignature>
|
|
irgen::getTypeAndGenericSignatureForManglingOutlineFunction(SILType type) {
|
|
auto loweredType = type.getASTType();
|
|
if (!loweredType->hasArchetype()) return {loweredType, nullptr};
|
|
|
|
// Find a non-local, non-opaque archetype in the type and pull out
|
|
// its generic environment.
|
|
// TODO: we ought to be able to usefully minimize this
|
|
|
|
GenericEnvironment *env = nullptr;
|
|
loweredType.findIf([&env](CanType t) -> bool {
|
|
if (auto arch = dyn_cast<ArchetypeType>(t)) {
|
|
if (!isa<PrimaryArchetypeType>(arch) &&
|
|
!isa<PackArchetypeType>(arch))
|
|
return false;
|
|
env = arch->getGenericEnvironment();
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
assert(env && "has archetype but no archetype?!");
|
|
return {loweredType->mapTypeOutOfContext()->getCanonicalType(),
|
|
env->getGenericSignature().getCanonicalSignature()};
|
|
}
|
|
|
|
void TypeInfo::callOutlinedCopy(IRGenFunction &IGF, Address dest, Address src,
|
|
SILType T, IsInitialization_t isInit,
|
|
IsTake_t isTake) const {
|
|
if (!IGF.IGM.getOptions().UseTypeLayoutValueHandling) {
|
|
OutliningMetadataCollector collector(IGF);
|
|
if (T.hasArchetype()) {
|
|
collectMetadataForOutlining(collector, T);
|
|
}
|
|
collector.emitCallToOutlinedCopy(dest, src, T, *this, isInit, isTake);
|
|
return;
|
|
}
|
|
|
|
if (!T.hasArchetype()) {
|
|
// Call the outlined copy function (the implementation will call vwt in this
|
|
// case).
|
|
OutliningMetadataCollector collector(IGF);
|
|
collector.emitCallToOutlinedCopy(dest, src, T, *this, isInit, isTake);
|
|
return;
|
|
}
|
|
|
|
if (isInit == IsInitialization && isTake == IsTake) {
|
|
return emitInitializeWithTakeCall(IGF, T, dest, src);
|
|
} else if (isInit == IsInitialization && isTake == IsNotTake) {
|
|
return emitInitializeWithCopyCall(IGF, T, dest, src);
|
|
} else if (isInit == IsNotInitialization && isTake == IsTake) {
|
|
return emitAssignWithTakeCall(IGF, T, dest, src);
|
|
} else if (isInit == IsNotInitialization && isTake == IsNotTake) {
|
|
return emitAssignWithCopyCall(IGF, T, dest, src);
|
|
}
|
|
llvm_unreachable("unknown case");
|
|
}
|
|
|
|
void OutliningMetadataCollector::emitCallToOutlinedCopy(
|
|
Address dest, Address src,
|
|
SILType T, const TypeInfo &ti,
|
|
IsInitialization_t isInit, IsTake_t isTake) const {
|
|
llvm::SmallVector<llvm::Value *, 4> args;
|
|
args.push_back(IGF.Builder.CreateElementBitCast(src, ti.getStorageType())
|
|
.getAddress());
|
|
args.push_back(IGF.Builder.CreateElementBitCast(dest, ti.getStorageType())
|
|
.getAddress());
|
|
addMetadataArguments(args);
|
|
|
|
llvm::Constant *outlinedFn;
|
|
if (isInit && isTake) {
|
|
outlinedFn =
|
|
IGF.IGM.getOrCreateOutlinedInitializeWithTakeFunction(T, ti, *this);
|
|
} else if (isInit) {
|
|
outlinedFn =
|
|
IGF.IGM.getOrCreateOutlinedInitializeWithCopyFunction(T, ti, *this);
|
|
} else if (isTake) {
|
|
outlinedFn =
|
|
IGF.IGM.getOrCreateOutlinedAssignWithTakeFunction(T, ti, *this);
|
|
} else {
|
|
outlinedFn =
|
|
IGF.IGM.getOrCreateOutlinedAssignWithCopyFunction(T, ti, *this);
|
|
}
|
|
|
|
llvm::CallInst *call = IGF.Builder.CreateCall(
|
|
cast<llvm::Function>(outlinedFn)->getFunctionType(), outlinedFn, args);
|
|
call->setCallingConv(IGF.IGM.DefaultCC);
|
|
}
|
|
|
|
static bool needsSpecialOwnershipHandling(SILType t) {
|
|
auto astType = t.getASTType();
|
|
auto ref = dyn_cast<ReferenceStorageType>(astType);
|
|
if (!ref) {
|
|
return false;
|
|
}
|
|
return ref->getOwnership() != ReferenceOwnership::Strong;
|
|
}
|
|
|
|
bool isTypeMetadataForLayoutAccessible(SILModule &M, SILType type);
|
|
|
|
static bool canUseValueWitnessForValueOp(IRGenModule &IGM, SILType T) {
|
|
if (!IGM.getSILModule().isTypeMetadataForLayoutAccessible(T))
|
|
return false;
|
|
|
|
// It is not a good code size trade-off to instantiate a metatype for
|
|
// existentials, and also does not back-deploy gracefully in the case of
|
|
// constrained protocols.
|
|
if (T.getASTType()->isExistentialType())
|
|
return false;
|
|
|
|
if (needsSpecialOwnershipHandling(T))
|
|
return false;
|
|
if (T.getASTType()->hasDynamicSelfType())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getOrCreateOutlinedInitializeWithTakeFunction(
|
|
SILType T, const TypeInfo &ti,
|
|
const OutliningMetadataCollector &collector) {
|
|
auto manglingBits = getTypeAndGenericSignatureForManglingOutlineFunction(T);
|
|
auto funcName =
|
|
IRGenMangler().mangleOutlinedInitializeWithTakeFunction(manglingBits.first,
|
|
manglingBits.second);
|
|
|
|
return getOrCreateOutlinedCopyAddrHelperFunction(
|
|
T, ti, collector, funcName,
|
|
[this](IRGenFunction &IGF, Address dest, Address src, SILType T,
|
|
const TypeInfo &ti) {
|
|
if (!IGF.IGM.getOptions().UseTypeLayoutValueHandling ||
|
|
T.hasArchetype() || !canUseValueWitnessForValueOp(*this, T)) {
|
|
ti.initializeWithTake(IGF, dest, src, T, true);
|
|
} else {
|
|
emitInitializeWithTakeCall(IGF, T, dest, src);
|
|
}
|
|
});
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getOrCreateOutlinedInitializeWithCopyFunction(
|
|
SILType T, const TypeInfo &ti,
|
|
const OutliningMetadataCollector &collector) {
|
|
auto manglingBits = getTypeAndGenericSignatureForManglingOutlineFunction(T);
|
|
auto funcName =
|
|
IRGenMangler().mangleOutlinedInitializeWithCopyFunction(manglingBits.first,
|
|
manglingBits.second);
|
|
|
|
return getOrCreateOutlinedCopyAddrHelperFunction(
|
|
T, ti, collector, funcName,
|
|
[this](IRGenFunction &IGF, Address dest, Address src, SILType T,
|
|
const TypeInfo &ti) {
|
|
if (!IGF.IGM.getOptions().UseTypeLayoutValueHandling ||
|
|
T.hasArchetype() || !canUseValueWitnessForValueOp(*this, T)) {
|
|
ti.initializeWithCopy(IGF, dest, src, T, true);
|
|
} else {
|
|
emitInitializeWithCopyCall(IGF, T, dest, src);
|
|
}
|
|
});
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getOrCreateOutlinedAssignWithTakeFunction(
|
|
SILType T, const TypeInfo &ti,
|
|
const OutliningMetadataCollector &collector) {
|
|
auto manglingBits = getTypeAndGenericSignatureForManglingOutlineFunction(T);
|
|
auto funcName =
|
|
IRGenMangler().mangleOutlinedAssignWithTakeFunction(manglingBits.first,
|
|
manglingBits.second);
|
|
|
|
return getOrCreateOutlinedCopyAddrHelperFunction(
|
|
T, ti, collector, funcName,
|
|
[this](IRGenFunction &IGF, Address dest, Address src, SILType T,
|
|
const TypeInfo &ti) {
|
|
if (!IGF.IGM.getOptions().UseTypeLayoutValueHandling ||
|
|
T.hasArchetype() || !canUseValueWitnessForValueOp(*this, T)) {
|
|
ti.assignWithTake(IGF, dest, src, T, true);
|
|
} else {
|
|
emitAssignWithTakeCall(IGF, T, dest, src);
|
|
}
|
|
});
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getOrCreateOutlinedAssignWithCopyFunction(
|
|
SILType T, const TypeInfo &ti,
|
|
const OutliningMetadataCollector &collector) {
|
|
auto manglingBits = getTypeAndGenericSignatureForManglingOutlineFunction(T);
|
|
auto funcName =
|
|
IRGenMangler().mangleOutlinedAssignWithCopyFunction(manglingBits.first,
|
|
manglingBits.second);
|
|
|
|
return getOrCreateOutlinedCopyAddrHelperFunction(
|
|
T, ti, collector, funcName,
|
|
[this](IRGenFunction &IGF, Address dest, Address src, SILType T,
|
|
const TypeInfo &ti) {
|
|
if (!IGF.IGM.getOptions().UseTypeLayoutValueHandling ||
|
|
T.hasArchetype() || !canUseValueWitnessForValueOp(*this, T)) {
|
|
ti.assignWithCopy(IGF, dest, src, T, true);
|
|
} else {
|
|
emitAssignWithCopyCall(IGF, T, dest, src);
|
|
}
|
|
});
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getOrCreateOutlinedCopyAddrHelperFunction(
|
|
SILType T, const TypeInfo &ti,
|
|
const OutliningMetadataCollector &collector,
|
|
StringRef funcName,
|
|
CopyAddrHelperGenerator generator) {
|
|
auto ptrTy = ti.getStorageType()->getPointerTo();
|
|
|
|
llvm::SmallVector<llvm::Type *, 4> paramTys;
|
|
paramTys.push_back(ptrTy);
|
|
paramTys.push_back(ptrTy);
|
|
collector.addMetadataParameterTypes(paramTys);
|
|
|
|
return getOrCreateHelperFunction(funcName, ptrTy, paramTys,
|
|
[&](IRGenFunction &IGF) {
|
|
auto params = IGF.collectParameters();
|
|
Address src = ti.getAddressForPointer(params.claimNext());
|
|
Address dest = ti.getAddressForPointer(params.claimNext());
|
|
collector.bindMetadataParameters(IGF, params);
|
|
generator(IGF, dest, src, T, ti);
|
|
IGF.Builder.CreateRet(dest.getAddress());
|
|
},
|
|
true /*setIsNoInline*/);
|
|
}
|
|
|
|
void TypeInfo::callOutlinedDestroy(IRGenFunction &IGF,
|
|
Address addr, SILType T) const {
|
|
// Short-cut destruction of trivial values.
|
|
if (IGF.IGM.getTypeLowering(T).isTrivial())
|
|
return;
|
|
|
|
if (!IGF.IGM.getOptions().UseTypeLayoutValueHandling) {
|
|
OutliningMetadataCollector collector(IGF);
|
|
if (T.hasArchetype()) {
|
|
collectMetadataForOutlining(collector, T);
|
|
}
|
|
collector.emitCallToOutlinedDestroy(addr, T, *this);
|
|
return;
|
|
}
|
|
|
|
if (!T.hasArchetype()) {
|
|
// Call the outlined copy function (the implementation will call vwt in this
|
|
// case).
|
|
OutliningMetadataCollector collector(IGF);
|
|
collector.emitCallToOutlinedDestroy(addr, T, *this);
|
|
return;
|
|
}
|
|
|
|
return emitDestroyCall(IGF, T, addr);
|
|
}
|
|
|
|
void OutliningMetadataCollector::emitCallToOutlinedDestroy(
|
|
Address addr, SILType T, const TypeInfo &ti) const {
|
|
llvm::SmallVector<llvm::Value *, 4> args;
|
|
args.push_back(IGF.Builder.CreateElementBitCast(addr, ti.getStorageType())
|
|
.getAddress());
|
|
addMetadataArguments(args);
|
|
|
|
auto outlinedFn =
|
|
IGF.IGM.getOrCreateOutlinedDestroyFunction(T, ti, *this);
|
|
|
|
llvm::CallInst *call = IGF.Builder.CreateCall(
|
|
cast<llvm::Function>(outlinedFn)->getFunctionType(), outlinedFn, args);
|
|
call->setCallingConv(IGF.IGM.DefaultCC);
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getOrCreateOutlinedDestroyFunction(
|
|
SILType T, const TypeInfo &ti,
|
|
const OutliningMetadataCollector &collector) {
|
|
IRGenMangler mangler;
|
|
auto manglingBits = getTypeAndGenericSignatureForManglingOutlineFunction(T);
|
|
auto funcName = mangler.mangleOutlinedDestroyFunction(manglingBits.first,
|
|
manglingBits.second);
|
|
|
|
auto ptrTy = ti.getStorageType()->getPointerTo();
|
|
llvm::SmallVector<llvm::Type *, 4> paramTys;
|
|
paramTys.push_back(ptrTy);
|
|
collector.addMetadataParameterTypes(paramTys);
|
|
|
|
return getOrCreateHelperFunction(funcName, ptrTy, paramTys,
|
|
[&](IRGenFunction &IGF) {
|
|
Explosion params = IGF.collectParameters();
|
|
Address addr = ti.getAddressForPointer(params.claimNext());
|
|
collector.bindMetadataParameters(IGF, params);
|
|
if (!IGF.IGM.getOptions().UseTypeLayoutValueHandling ||
|
|
T.hasArchetype() || !canUseValueWitnessForValueOp(*this, T)) {
|
|
ti.destroy(IGF, addr, T, true);
|
|
} else {
|
|
emitDestroyCall(IGF, T, addr);
|
|
}
|
|
|
|
IGF.Builder.CreateRet(addr.getAddress());
|
|
},
|
|
true /*setIsNoInline*/);
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getOrCreateRetainFunction(const TypeInfo &ti,
|
|
SILType t,
|
|
llvm::Type *llvmType,
|
|
Atomicity atomicity) {
|
|
auto *loadableTI = cast<LoadableTypeInfo>(&ti);
|
|
IRGenMangler mangler;
|
|
auto manglingBits =
|
|
getTypeAndGenericSignatureForManglingOutlineFunction(t);
|
|
auto funcName = mangler.mangleOutlinedRetainFunction(manglingBits.first,
|
|
manglingBits.second);
|
|
llvm::Type *argTys[] = {llvmType};
|
|
return getOrCreateHelperFunction(
|
|
funcName, llvmType, argTys,
|
|
[&](IRGenFunction &IGF) {
|
|
auto it = IGF.CurFn->arg_begin();
|
|
Address addr(&*it++, loadableTI->getStorageType(),
|
|
loadableTI->getFixedAlignment());
|
|
Explosion loaded;
|
|
loadableTI->loadAsTake(IGF, addr, loaded);
|
|
Explosion out;
|
|
loadableTI->copy(IGF, loaded, out, atomicity);
|
|
(void)out.claimAll();
|
|
IGF.Builder.CreateRet(addr.getAddress());
|
|
},
|
|
true /*setIsNoInline*/);
|
|
}
|
|
|
|
llvm::Constant *
|
|
IRGenModule::getOrCreateReleaseFunction(const TypeInfo &ti,
|
|
SILType t,
|
|
llvm::Type *llvmType,
|
|
Atomicity atomicity) {
|
|
auto *loadableTI = cast<LoadableTypeInfo>(&ti);
|
|
IRGenMangler mangler;
|
|
auto manglingBits =
|
|
getTypeAndGenericSignatureForManglingOutlineFunction(t);
|
|
auto funcName = mangler.mangleOutlinedReleaseFunction(manglingBits.first,
|
|
manglingBits.second);
|
|
llvm::Type *argTys[] = {llvmType};
|
|
return getOrCreateHelperFunction(
|
|
funcName, llvmType, argTys,
|
|
[&](IRGenFunction &IGF) {
|
|
auto it = IGF.CurFn->arg_begin();
|
|
Address addr(&*it++, loadableTI->getStorageType(),
|
|
loadableTI->getFixedAlignment());
|
|
Explosion loaded;
|
|
loadableTI->loadAsTake(IGF, addr, loaded);
|
|
loadableTI->consume(IGF, loaded, atomicity, t);
|
|
IGF.Builder.CreateRet(addr.getAddress());
|
|
},
|
|
true /*setIsNoInline*/);
|
|
}
|