mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
304 lines
13 KiB
C++
304 lines
13 KiB
C++
//===--- SILFunctionBuilder.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SIL/SILFunctionBuilder.h"
|
|
#include "swift/AST/AttrKind.h"
|
|
#include "swift/AST/Availability.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/SemanticAttrs.h"
|
|
|
|
using namespace swift;
|
|
|
|
SILFunction *SILFunctionBuilder::getOrCreateFunction(
|
|
SILLocation loc, StringRef name, SILLinkage linkage, CanSILFunctionType type, IsBare_t isBareSILFunction,
|
|
IsTransparent_t isTransparent, IsSerialized_t isSerialized,
|
|
IsDynamicallyReplaceable_t isDynamic, ProfileCounter entryCount,
|
|
IsThunk_t isThunk, SubclassScope subclassScope) {
|
|
assert(!type->isNoEscape() && "Function decls always have escaping types.");
|
|
if (auto fn = mod.lookUpFunction(name)) {
|
|
assert(fn->getLoweredFunctionType() == type);
|
|
assert(stripExternalFromLinkage(fn->getLinkage()) ==
|
|
stripExternalFromLinkage(linkage));
|
|
return fn;
|
|
}
|
|
|
|
auto fn = SILFunction::create(mod, linkage, name, type, nullptr, loc,
|
|
isBareSILFunction, isTransparent, isSerialized,
|
|
entryCount, isDynamic, IsNotExactSelfClass,
|
|
isThunk, subclassScope);
|
|
fn->setDebugScope(new (mod) SILDebugScope(loc, fn));
|
|
return fn;
|
|
}
|
|
|
|
void SILFunctionBuilder::addFunctionAttributes(
|
|
SILFunction *F, DeclAttributes &Attrs, SILModule &M,
|
|
llvm::function_ref<SILFunction *(SILLocation loc, SILDeclRef constant)>
|
|
getOrCreateDeclaration,
|
|
SILDeclRef constant) {
|
|
|
|
for (auto *A : Attrs.getAttributes<SemanticsAttr>())
|
|
F->addSemanticsAttr(cast<SemanticsAttr>(A)->Value);
|
|
|
|
// If we are asked to emit assembly vision remarks for this function, mark the
|
|
// function as force emitting all optremarks including assembly vision
|
|
// remarks. This allows us to emit the assembly vision remarks without needing
|
|
// to change any of the underlying optremark mechanisms.
|
|
if (auto *A = Attrs.getAttribute(DAK_EmitAssemblyVisionRemarks))
|
|
F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
|
|
|
|
// Propagate @_specialize.
|
|
for (auto *A : Attrs.getAttributes<SpecializeAttr>()) {
|
|
auto *SA = cast<SpecializeAttr>(A);
|
|
auto kind =
|
|
SA->getSpecializationKind() == SpecializeAttr::SpecializationKind::Full
|
|
? SILSpecializeAttr::SpecializationKind::Full
|
|
: SILSpecializeAttr::SpecializationKind::Partial;
|
|
assert(!constant.isNull());
|
|
SILFunction *targetFunction = nullptr;
|
|
auto *attributedFuncDecl = constant.getDecl();
|
|
auto *targetFunctionDecl = SA->getTargetFunctionDecl(attributedFuncDecl);
|
|
// Filter out _spi.
|
|
auto spiGroups = SA->getSPIGroups();
|
|
bool hasSPI = !spiGroups.empty();
|
|
if (hasSPI) {
|
|
if (attributedFuncDecl->getModuleContext() != M.getSwiftModule() &&
|
|
!M.getSwiftModule()->isImportedAsSPI(SA, attributedFuncDecl)) {
|
|
continue;
|
|
}
|
|
}
|
|
assert(spiGroups.size() <= 1 && "SIL does not support multiple SPI groups");
|
|
Identifier spiGroupIdent;
|
|
if (hasSPI) {
|
|
spiGroupIdent = spiGroups[0];
|
|
}
|
|
auto availability =
|
|
AvailabilityInference::annotatedAvailableRangeForAttr(SA,
|
|
M.getSwiftModule()->getASTContext());
|
|
if (targetFunctionDecl) {
|
|
SILDeclRef declRef(targetFunctionDecl, constant.kind, false);
|
|
targetFunction = getOrCreateDeclaration(targetFunctionDecl, declRef);
|
|
F->addSpecializeAttr(SILSpecializeAttr::create(
|
|
M, SA->getSpecializedSignature(), SA->isExported(), kind,
|
|
targetFunction, spiGroupIdent,
|
|
attributedFuncDecl->getModuleContext(), availability));
|
|
} else {
|
|
F->addSpecializeAttr(SILSpecializeAttr::create(
|
|
M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr,
|
|
spiGroupIdent, attributedFuncDecl->getModuleContext(), availability));
|
|
}
|
|
}
|
|
|
|
if (auto *OA = Attrs.getAttribute<OptimizeAttr>()) {
|
|
F->setOptimizationMode(OA->getMode());
|
|
}
|
|
|
|
// @_silgen_name and @_cdecl functions may be called from C code somewhere.
|
|
if (Attrs.hasAttribute<SILGenNameAttr>() || Attrs.hasAttribute<CDeclAttr>())
|
|
F->setHasCReferences(true);
|
|
|
|
if (Attrs.hasAttribute<NoLocksAttr>()) {
|
|
F->setPerfConstraints(PerformanceConstraints::NoLocks);
|
|
} else if (Attrs.hasAttribute<NoAllocationAttr>()) {
|
|
F->setPerfConstraints(PerformanceConstraints::NoAllocation);
|
|
}
|
|
|
|
// Validate `@differentiable` attributes by calling `getParameterIndices`.
|
|
// This is important for:
|
|
// - Skipping invalid `@differentiable` attributes in non-primary files.
|
|
// - Preventing duplicate SIL differentiability witness creation for
|
|
// `@differentiable` attributes on `AbstractStorageDecl` declarations.
|
|
// Such `@differentiable` attributes are deleted and recreated on the getter
|
|
// `AccessorDecl` of the `AbstractStorageDecl`.
|
|
for (auto *A : Attrs.getAttributes<DifferentiableAttr>())
|
|
(void)A->getParameterIndices();
|
|
|
|
// Propagate `@noDerivative` as `[_semantics "autodiff.nonvarying"]`.
|
|
//
|
|
// `@noDerivative` implies non-varying semantics for differentiable activity
|
|
// analysis. SIL values produced from references to `@noDerivative`
|
|
// declarations will not be marked as varying; these values do not need a
|
|
// derivative.
|
|
if (Attrs.hasAttribute<NoDerivativeAttr>())
|
|
F->addSemanticsAttr("autodiff.nonvarying");
|
|
|
|
// Propagate @_dynamicReplacement(for:).
|
|
if (constant.isNull())
|
|
return;
|
|
auto *decl = constant.getDecl();
|
|
|
|
// Only emit replacements for the objc entry point of objc methods.
|
|
// There is one exception: @_dynamicReplacement(for:) of @objc methods in
|
|
// generic classes. In this special case we use native replacement instead of
|
|
// @objc categories.
|
|
if (decl->isObjC() && !decl->isNativeMethodReplacement() &&
|
|
F->getLoweredFunctionType()->getExtInfo().getRepresentation() !=
|
|
SILFunctionTypeRepresentation::ObjCMethod)
|
|
return;
|
|
|
|
// Only assign replacements when the thing being replaced is function-like and
|
|
// explicitly declared.
|
|
auto *origDecl = decl->getDynamicallyReplacedDecl();
|
|
auto *replacedDecl = dyn_cast_or_null<AbstractFunctionDecl>(origDecl);
|
|
if (!replacedDecl)
|
|
return;
|
|
|
|
// For @objc method replacement we normally use categories to perform the
|
|
// replacement. Except for methods in generic class where we can't. Instead,
|
|
// we special case this and use the native swift replacement mechanism.
|
|
if (decl->isObjC() && !decl->isNativeMethodReplacement()) {
|
|
F->setObjCReplacement(replacedDecl);
|
|
return;
|
|
}
|
|
|
|
if (!constant.canBeDynamicReplacement())
|
|
return;
|
|
|
|
SILDeclRef declRef(replacedDecl, constant.kind, false);
|
|
auto *replacedFunc = getOrCreateDeclaration(replacedDecl, declRef);
|
|
|
|
assert(replacedFunc->getLoweredFunctionType() ==
|
|
F->getLoweredFunctionType() ||
|
|
replacedFunc->getLoweredFunctionType()->hasOpaqueArchetype());
|
|
|
|
F->setDynamicallyReplacedFunction(replacedFunc);
|
|
}
|
|
|
|
SILFunction *SILFunctionBuilder::getOrCreateFunction(
|
|
SILLocation loc, SILDeclRef constant, ForDefinition_t forDefinition,
|
|
llvm::function_ref<SILFunction *(SILLocation loc, SILDeclRef constant)>
|
|
getOrCreateDeclaration,
|
|
ProfileCounter entryCount) {
|
|
auto nameTmp = constant.mangle();
|
|
auto constantType = mod.Types.getConstantFunctionType(
|
|
TypeExpansionContext::minimal(), constant);
|
|
SILLinkage linkage = constant.getLinkage(forDefinition);
|
|
|
|
if (auto fn = mod.lookUpFunction(nameTmp)) {
|
|
// During SILGen (where the module's SIL stage is Raw), there might be
|
|
// mismatches between the type or linkage. This can happen, when two
|
|
// functions are mistakenly mapped to the same name (e.g. with @_cdecl).
|
|
// We want to issue a regular error in this case and not crash with an
|
|
// assert.
|
|
assert(mod.getStage() == SILStage::Raw ||
|
|
fn->getLoweredFunctionType() == constantType);
|
|
auto linkageForDef = constant.getLinkage(ForDefinition_t::ForDefinition);
|
|
auto fnLinkage = fn->getLinkage();
|
|
assert(mod.getStage() == SILStage::Raw || fn->getLinkage() == linkage ||
|
|
(forDefinition == ForDefinition_t::NotForDefinition &&
|
|
(fnLinkage == linkageForDef ||
|
|
(linkageForDef == SILLinkage::PublicNonABI &&
|
|
fnLinkage == SILLinkage::SharedExternal))));
|
|
if (forDefinition) {
|
|
// In all the cases where getConstantLinkage returns something
|
|
// different for ForDefinition, it returns an available-externally
|
|
// linkage.
|
|
if (isAvailableExternally(fn->getLinkage())) {
|
|
fn->setLinkage(constant.getLinkage(ForDefinition));
|
|
}
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
IsTransparent_t IsTrans =
|
|
constant.isTransparent() ? IsTransparent : IsNotTransparent;
|
|
IsSerialized_t IsSer = constant.isSerialized();
|
|
|
|
EffectsKind EK = constant.hasEffectsAttribute()
|
|
? constant.getEffectsAttribute()
|
|
: EffectsKind::Unspecified;
|
|
|
|
Inline_t inlineStrategy = InlineDefault;
|
|
if (constant.isNoinline())
|
|
inlineStrategy = NoInline;
|
|
else if (constant.isAlwaysInline())
|
|
inlineStrategy = AlwaysInline;
|
|
|
|
StringRef name = mod.allocateCopy(nameTmp);
|
|
IsDynamicallyReplaceable_t IsDyn = IsNotDynamic;
|
|
if (constant.isDynamicallyReplaceable()) {
|
|
IsDyn = IsDynamic;
|
|
IsTrans = IsNotTransparent;
|
|
}
|
|
|
|
auto *F = SILFunction::create(mod, linkage, name, constantType, nullptr, None,
|
|
IsNotBare, IsTrans, IsSer, entryCount, IsDyn,
|
|
IsNotExactSelfClass,
|
|
IsNotThunk, constant.getSubclassScope(),
|
|
inlineStrategy, EK);
|
|
F->setDebugScope(new (mod) SILDebugScope(loc, F));
|
|
|
|
if (constant.isGlobal())
|
|
F->setSpecialPurpose(SILFunction::Purpose::GlobalInit);
|
|
|
|
if (constant.hasDecl()) {
|
|
auto decl = constant.getDecl();
|
|
|
|
if (constant.isForeign && decl->hasClangNode())
|
|
F->setClangNodeOwner(decl);
|
|
|
|
F->setAvailabilityForLinkage(decl->getAvailabilityForLinkage());
|
|
F->setAlwaysWeakImported(decl->isAlwaysWeakImported());
|
|
|
|
if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
|
|
auto *storage = accessor->getStorage();
|
|
// Add attributes for e.g. computed properties.
|
|
addFunctionAttributes(F, storage->getAttrs(), mod,
|
|
getOrCreateDeclaration);
|
|
|
|
auto *varDecl = dyn_cast<VarDecl>(storage);
|
|
if (varDecl && varDecl->getAttrs().hasAttribute<LazyAttr>() &&
|
|
accessor->getAccessorKind() == AccessorKind::Get) {
|
|
F->setSpecialPurpose(SILFunction::Purpose::LazyPropertyGetter);
|
|
|
|
// Lazy property getters should not get inlined because they are usually
|
|
// non-tivial functions (otherwise the user would not implement it as
|
|
// lazy property). Inlining such getters would most likely not benefit
|
|
// other optimizations because the top-level switch_enum cannot be
|
|
// constant folded in most cases.
|
|
// Also, not inlining lazy property getters enables optimizing them in
|
|
// CSE.
|
|
F->setInlineStrategy(NoInline);
|
|
}
|
|
}
|
|
addFunctionAttributes(F, decl->getAttrs(), mod, getOrCreateDeclaration,
|
|
constant);
|
|
}
|
|
|
|
return F;
|
|
}
|
|
|
|
SILFunction *SILFunctionBuilder::getOrCreateSharedFunction(
|
|
SILLocation loc, StringRef name, CanSILFunctionType type,
|
|
IsBare_t isBareSILFunction, IsTransparent_t isTransparent,
|
|
IsSerialized_t isSerialized, ProfileCounter entryCount, IsThunk_t isThunk,
|
|
IsDynamicallyReplaceable_t isDynamic) {
|
|
return getOrCreateFunction(loc, name, SILLinkage::Shared, type,
|
|
isBareSILFunction, isTransparent, isSerialized,
|
|
isDynamic, entryCount, isThunk,
|
|
SubclassScope::NotApplicable);
|
|
}
|
|
|
|
SILFunction *SILFunctionBuilder::createFunction(
|
|
SILLinkage linkage, StringRef name, CanSILFunctionType loweredType,
|
|
GenericEnvironment *genericEnv, Optional<SILLocation> loc,
|
|
IsBare_t isBareSILFunction, IsTransparent_t isTrans,
|
|
IsSerialized_t isSerialized, IsDynamicallyReplaceable_t isDynamic,
|
|
ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope,
|
|
Inline_t inlineStrategy, EffectsKind EK, SILFunction *InsertBefore,
|
|
const SILDebugScope *DebugScope) {
|
|
return SILFunction::create(mod, linkage, name, loweredType, genericEnv, loc,
|
|
isBareSILFunction, isTrans, isSerialized,
|
|
entryCount, isDynamic, IsNotExactSelfClass,
|
|
isThunk, subclassScope,
|
|
inlineStrategy, EK, InsertBefore, DebugScope);
|
|
}
|