//===--- 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/ASTMangler.h" #include "swift/AST/AttrKind.h" #include "swift/AST/AvailabilityInference.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/DistributedDecl.h" #include "swift/AST/ParameterList.h" #include "swift/AST/SemanticAttrs.h" #include "swift/Basic/Assertions.h" #include "clang/AST/Mangle.h" using namespace swift; SILFunction *SILFunctionBuilder::getOrCreateFunction( SILLocation loc, StringRef name, SILLinkage linkage, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, SerializedKind_t serializedKind, IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed, IsRuntimeAccessible_t isRuntimeAccessible, 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) || mod.getOptions().EmbeddedSwift); return fn; } auto fn = SILFunction::create(mod, linkage, name, type, nullptr, loc, isBareSILFunction, isTransparent, serializedKind, entryCount, isDynamic, isDistributed, isRuntimeAccessible, IsNotExactSelfClass, isThunk, subclassScope); fn->setDebugScope(new (mod) SILDebugScope(loc, fn)); return fn; } void SILFunctionBuilder::addFunctionAttributes( SILFunction *F, DeclAttributes &Attrs, SILModule &M, llvm::function_ref getOrCreateDeclaration, SILDeclRef constant) { for (auto *A : Attrs.getAttributes()) F->addSemanticsAttr(cast(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 (Attrs.getAttribute(DeclAttrKind::EmitAssemblyVisionRemarks)) F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX); // Propagate @_specialize. for (auto *A : Attrs.getAttributes()) { auto *SA = cast(A); auto kind = SA->getSpecializationKind() == SpecializeAttr::SpecializationKind::Full ? SILSpecializeAttr::SpecializationKind::Full : SILSpecializeAttr::SpecializationKind::Partial; assert(!constant.isNull()); SILFunction *targetFunction = nullptr; auto *attributedFuncDecl = constant.getAbstractFunctionDecl(); 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( attributedFuncDecl, SA, M.getSwiftModule()->getASTContext()); auto specializedSignature = SA->getSpecializedSignature(attributedFuncDecl); if (targetFunctionDecl) { SILDeclRef declRef(targetFunctionDecl, constant.kind, false); targetFunction = getOrCreateDeclaration(targetFunctionDecl, declRef); F->addSpecializeAttr(SILSpecializeAttr::create( M, specializedSignature, SA->getTypeErasedParams(), SA->isExported(), kind, targetFunction, spiGroupIdent, attributedFuncDecl->getModuleContext(), availability)); } else { F->addSpecializeAttr(SILSpecializeAttr::create( M, specializedSignature, SA->getTypeErasedParams(), SA->isExported(), kind, nullptr, spiGroupIdent, attributedFuncDecl->getModuleContext(), availability)); } } llvm::SmallVector customEffects; if (constant) { for (auto *attr : Attrs.getAttributes()) { auto *effectsAttr = cast(attr); if (effectsAttr->getKind() == EffectsKind::Custom) { customEffects.push_back(effectsAttr); continue; } if (F->getEffectsKind() != EffectsKind::Unspecified) { // If multiple known effects are specified, the most restrictive one // is used. F->setEffectsKind( std::min(effectsAttr->getKind(), F->getEffectsKind())); } else { F->setEffectsKind(effectsAttr->getKind()); } } } if (!customEffects.empty()) { llvm::SmallVector paramNames; auto *fnDecl = cast(constant.getDecl()); if (ParameterList *paramList = fnDecl->getParameters()) { for (ParamDecl *pd : *paramList) { // Give up on tuples. Their elements are added as individual // arguments. It destroys the 1-1 relation ship between parameters // and arguments. if (pd->getInterfaceType()->is()) break; // First try the "local" parameter name. If there is none, use the // API name. E.g. `foo(apiName localName: Type) {}` StringRef name = pd->getName().str(); if (name.empty()) name = pd->getArgumentName().str(); if (!name.empty()) paramNames.push_back(name); } } for (const EffectsAttr *effectsAttr : llvm::reverse(customEffects)) { auto error = F->parseArgumentEffectsFromSource( effectsAttr->getCustomString(), paramNames); if (error.first) { SourceLoc loc = effectsAttr->getCustomStringLocation(); if (loc.isValid()) loc = loc.getAdvancedLoc(error.second); mod.getASTContext().Diags.diagnose(loc, diag::warning_in_effects_attribute, StringRef(error.first)); } } } if (auto *OA = Attrs.getAttribute()) { F->setOptimizationMode(OA->getMode()); } // @_silgen_name and @_cdecl functions may be called from C code somewhere. if (Attrs.hasAttribute() || Attrs.hasAttribute()) F->setHasCReferences(true); for (auto *EA : Attrs.getAttributes()) { bool shouldExportDecl = true; if (Attrs.hasAttribute()) { // If the function is marked with @cdecl, expose only C compatible // thunk function. shouldExportDecl = constant.isNativeToForeignThunk(); } if (EA->getExposureKind() == ExposureKind::Wasm && shouldExportDecl) { // A wasm-level exported function must be retained if it appears in a // compilation unit. F->setMarkedAsUsed(true); if (EA->Name.empty()) F->setWasmExportName(F->getName()); else F->setWasmExportName(EA->Name); } } if (auto *EA = ExternAttr::find(Attrs, ExternKind::Wasm)) { // @_extern(wasm) always has explicit names F->setWasmImportModuleAndField(*EA->ModuleName, *EA->Name); } if (Attrs.hasAttribute()) F->setMarkedAsUsed(true); if (Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::NoLocks); } else if (Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::NoAllocation); } else if (Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::NoRuntime); } else if (Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::NoExistentials); } else if (Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::NoObjCBridging); } if (Attrs.hasAttribute()) { F->setForceEnableLexicalLifetimes(DoForceEnableLexicalLifetimes); } if (Attrs.hasAttribute()) { F->setHasUnsafeNonEscapableResult(true); } // 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()) (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()) F->addSemanticsAttr("autodiff.nonvarying"); // Propagate @_dynamicReplacement(for:). if (constant.isNull()) return; auto *decl = constant.getDecl(); // Don't add section for addressor functions (where decl is a global) if (isa(decl)) { if (auto *SA = Attrs.getAttribute()) F->setSection(SA->Name); } // 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(); if (auto *replacedDecl = dyn_cast_or_null(origDecl)) { // 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()) { SILDeclRef declRef(replacedDecl, constant.kind, false); auto *replacedFunc = getOrCreateDeclaration(replacedDecl, declRef); assert(replacedFunc->getLoweredFunctionType() == F->getLoweredFunctionType() || replacedFunc->getLoweredFunctionType()->hasOpaqueArchetype()); F->setDynamicallyReplacedFunction(replacedFunc); } } else if (constant.isDistributedThunk()) { // It's okay for `decodeFuncDecl` to be null because system could be // generic. if (auto decodeFuncDecl = getAssociatedDistributedInvocationDecoderDecodeNextArgumentFunction( decl)) { auto decodeRef = SILDeclRef(decodeFuncDecl); auto *adHocFunc = getOrCreateDeclaration(decodeFuncDecl, decodeRef); F->setReferencedAdHocRequirementWitnessFunction(adHocFunc); } } } SILFunction *SILFunctionBuilder::getOrCreateFunction( SILLocation loc, SILDeclRef constant, ForDefinition_t forDefinition, llvm::function_ref 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 || linkageForDef == SILLinkage::PackageNonABI) && fnLinkage == SILLinkage::Shared))); 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; SerializedKind_t IsSer = constant.getSerializedKind(); // Don't create a [serialized] function after serialization has happened. if (IsSer != IsNotSerialized && mod.isSerialized()) IsSer = IsNotSerialized; 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; } IsDistributed_t IsDistributed = IsDistributed_t::IsNotDistributed; // Mark both distributed thunks and methods as distributed. if (constant.hasFuncDecl() && constant.getFuncDecl()->isDistributed()) { IsDistributed = IsDistributed_t::IsDistributed; } IsRuntimeAccessible_t isRuntimeAccessible = IsNotRuntimeAccessible; auto *F = SILFunction::create( mod, linkage, name, constantType, nullptr, std::nullopt, IsNotBare, IsTrans, IsSer, entryCount, IsDyn, IsDistributed, isRuntimeAccessible, IsNotExactSelfClass, IsNotThunk, constant.getSubclassScope(), inlineStrategy); 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() && !decl->getObjCImplementationDecl()) F->setClangNodeOwner(decl); if (auto availability = constant.getAvailabilityForLinkage()) F->setAvailabilityForLinkage(*availability); F->setIsAlwaysWeakImported(decl->isAlwaysWeakImported()); if (auto *accessor = dyn_cast(decl)) { auto *storage = accessor->getStorage(); // Add attributes for e.g. computed properties. ASSERT(ABIRoleInfo(storage).providesAPI() && "addFunctionAttributes() on ABI-only accessor?"); addFunctionAttributes(F, storage->getAttrs(), mod, getOrCreateDeclaration); auto *varDecl = dyn_cast(storage); if (varDecl && varDecl->getAttrs().hasAttribute() && accessor->getAccessorKind() == AccessorKind::Get) { F->setSpecialPurpose(SILFunction::Purpose::LazyPropertyGetter); // Lazy property getters should not get inlined because they are usually // non-trivial 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); } } ASSERT(ABIRoleInfo(decl).providesAPI() && "addFunctionAttributes() on ABI-only decl?"); addFunctionAttributes(F, decl->getAttrs(), mod, getOrCreateDeclaration, constant); } return F; } SILFunction *SILFunctionBuilder::getOrCreateSharedFunction( SILLocation loc, StringRef name, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, SerializedKind_t serializedKind, ProfileCounter entryCount, IsThunk_t isThunk, IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed, IsRuntimeAccessible_t isRuntimeAccessible) { return getOrCreateFunction(loc, name, SILLinkage::Shared, type, isBareSILFunction, isTransparent, serializedKind, isDynamic, isDistributed, isRuntimeAccessible, entryCount, isThunk, SubclassScope::NotApplicable); } SILFunction *SILFunctionBuilder::createFunction( SILLinkage linkage, StringRef name, CanSILFunctionType loweredType, GenericEnvironment *genericEnv, std::optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, SerializedKind_t serializedKind, IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed, IsRuntimeAccessible_t isRuntimeAccessible, 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, serializedKind, entryCount, isDynamic, isDistributed, isRuntimeAccessible, IsNotExactSelfClass, isThunk, subclassScope, inlineStrategy, EK, InsertBefore, DebugScope); }